Update web-platform-tests to revision ad219567030d1f99f7310f52a17546b57b70d29e

This commit is contained in:
WPT Sync Bot 2019-02-14 20:36:47 -05:00
parent 2c63d1296b
commit a7e62acbe8
129 changed files with 4156 additions and 590 deletions

File diff suppressed because it is too large Load diff

View file

@ -3,3 +3,9 @@
[scroll-behavior: smooth on DIV element]
expected: FAIL
[Smooth scrolling while doing history navigation.]
expected: FAIL
[Instant scrolling while doing history navigation.]
expected: FAIL

View file

@ -0,0 +1,7 @@
[HTMLBaseElement.html]
[target on HTMLBaseElement must enqueue an attributeChanged reaction when adding a new attribute]
expected: FAIL
[target on HTMLBaseElement must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL

View file

@ -27,3 +27,24 @@
[Check if attribute serialization takes into account of the same prefix declared in an ancestor element]
expected: FAIL
[Check if the prefix of an attribute is replaced with another existing prefix mapped to the same namespace URI.]
expected: FAIL
[Check if inconsistent xmlns="..." is dropped.]
expected: FAIL
[Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix]
expected: FAIL
[Check if no special handling for XLink namespace unlike HTML serializer.]
expected: FAIL
[Check if the prefix of an attribute is NOT preserved in a case where neither its prefix nor its namespace URI is not already used.]
expected: FAIL
[Check if the prefix of an attribute is replaced with a generated one in a case where the prefix is already mapped to a different namespace URI.]
expected: FAIL
[Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix even if the prefix is assigned to another namespace.]
expected: FAIL

View file

@ -8,3 +8,12 @@
[Cross-site fetch]
expected: FAIL
[Same-origin mode]
expected: FAIL
[no-CORS mode]
expected: FAIL
[CORS mode]
expected: FAIL

View file

@ -8,3 +8,6 @@
[Cross-site script]
expected: FAIL
[Same-origin CORS script]
expected: FAIL

View file

@ -8,3 +8,6 @@
[Same-Origin style]
expected: FAIL
[Same-Origin, cors style]
expected: FAIL

View file

@ -9,3 +9,6 @@
[Cross-Site track]
expected: NOTRUN
[Same-Origin, CORS track]
expected: NOTRUN

View file

@ -1,5 +0,0 @@
[javascript-url-abort-return-value-undefined.tentative.html]
expected: TIMEOUT
[Not aborting fetch for javascript:undefined navigation]
expected: TIMEOUT

View file

@ -1,4 +0,0 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -0,0 +1,31 @@
[document_domain_access_details.sub.html]
[Access allowed if same-origin with no 'document.domain' modification. (Sanity check)]
expected: FAIL
[Access is revoked to Window object when we stop being same effective script origin due to document.domain.]
expected: FAIL
[Access allowed if different-origin but both set document.domain to parent domain.]
expected: FAIL
[Access is not revoked to random object when we stop being same effective script origin due to document.domain.]
expected: FAIL
[Access not allowed if different-origin with no 'document.domain' modification. (Sanity check)]
expected: FAIL
[Access disallowed again if same-origin, both set document-domain to existing value, then one sets to parent.]
expected: FAIL
[Access is revoked to Location object when we stop being same effective script origin due to document.domain.]
expected: FAIL
[Access allowed if same-origin and both set document.domain to existing value.]
expected: FAIL
[Access is not revoked to Document object when we stop being same effective script origin due to document.domain.]
expected: FAIL
[Access disallowed if same-origin but only one sets document.domain.]
expected: FAIL

View file

@ -0,0 +1,4 @@
[name-attribute.window.html]
[Window object's name IDL attribute]
expected: FAIL

View file

@ -1,4 +0,0 @@
[iframe_005.html]
[document.write external script into iframe write back into parent]
expected: FAIL

View file

@ -214,15 +214,11 @@ jobs:
- job: all_post
displayName: 'all tests (wpt.fyi hook)'
dependsOn: all_macOS
pool: server
pool:
vmImage: 'ubuntu-16.04'
steps:
- task: InvokeRESTAPI@1
- script: curl -s -S https://wpt.fyi/api/checks/azure/$(Build.BuildId)
displayName: 'Invoke wpt.fyi hook'
inputs:
serviceConnection: wpt.fyi
urlSuffix: /api/checks/azure/$(Build.BuildId)
- task: InvokeRESTAPI@1
- script: curl -s -S https://staging.wpt.fyi/api/checks/azure/$(Build.BuildId)
displayName: 'Invoke staging.wpt.fyi hook'
inputs:
serviceConnection: staging.wpt.fyi
urlSuffix: /api/checks/azure/$(Build.BuildId)
condition: succeededOrFailed()

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<title>Basic use of Worklet Animation</title>
<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<script src="common.js"></script>
<div id="target"></div>
<script>
promise_test(async t => {
await registerConstantLocalTimeAnimator(500);
const effect = new KeyframeEffect(target, [{ opacity: 0 }], { duration: 1000 });
const animation = new WorkletAnimation('constant_time', effect);
animation.play();
await waitForAsyncAnimationFrames(1);
assert_equals(getComputedStyle(target).opacity, "0.5");
}, "Simple worklet animation should output values at specified local time");
</script>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<title>Worklet Animation with options</title>
<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<script src="common.js"></script>
<div id="target"></div>
<script id="animate_with_options" type="text/worklet">
registerAnimator("test_animator", class {
constructor(options) {
this.time_ = options.time;
}
animate(currentTime, effect) {
effect.localTime = this.time_;
}
});
</script>
<script>
promise_test(async t => {
await runInAnimationWorklet(document.getElementById('animate_with_options').textContent);
const target = document.getElementById('target');
const effect = new KeyframeEffect(target, [{ opacity: 0 }], { duration: 1000 });
const options = {'time': 500};
const animation = new WorkletAnimation('test_animator', effect, document.timeline, options);
animation.play();
await waitForAsyncAnimationFrames(1);
assert_equals(getComputedStyle(target).opacity, "0.5");
}, "Animator should be able to use options to update the animation");
</script>

View file

@ -5,4 +5,8 @@ import time
sleep_padding = 15.0
def sleep_at_least(sleep_in_ms):
time.sleep((sleep_in_ms + sleep_padding) / 1E3);
sleep_until = time.time() + (sleep_in_ms / 1E3)
time.sleep((sleep_in_ms + sleep_padding) / 1E3)
# Check if the padding was sufficient; if not, sleep again.
while time.time() < sleep_until:
time.sleep(sleep_padding / 1E3)

View file

@ -36,7 +36,7 @@
.then(t.step_func(e => {
assert_equals(e.blockedURI, "inline");
assert_equals(e.lineNumber, 118);
assert_equals(e.columnNumber, 4);
assert_in_array(e.columnNumber, [4, 6]);
assert_equals(e.target, document, "Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.");
return watcher.wait_for('securitypolicyviolation');
}))
@ -50,7 +50,7 @@
.then(t.step_func(e => {
assert_equals(e.blockedURI, "inline");
assert_equals(e.lineNumber, 139);
assert_equals(e.columnNumber, 4);
assert_in_array(e.columnNumber, [4, 6]);
assert_equals(e.target, document, "Inline event handlers for disconnected elements target the document.");
return watcher.wait_for('securitypolicyviolation');
}))

View file

@ -0,0 +1,140 @@
<!DOCTYPE html>
<title>CSS Position: position:absolute dynamic containing block</title>
<link rel="author" title="mailto:atotic@google.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<link rel="help" href="https://drafts.csswg.org/css-position-3/#def-cb">
<meta name="assert" content="Tests changes in containing block for out-of-flow positioned descendants.">
<style>
#outer {
width: 400px;
height: 300px;
outline: black solid 1px;
}
#intermediate {
width: 300px;
height: 200px;
outline: gray solid 1px;
}
#target {
background: green;
}
.abs {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.fixed {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.abs-container {
position: relative;
}
.fixed-container {
will-change: transform;
}
div {
padding: 5px;
}
</style>
<!-- This tests potential caching of out-of-flow positioned descendants. -->
<div id="outer">
<div>
<div id="intermediate">
<div>
<div id="target">TTT</div>
<div id="noLayout1"></div>
</div>
<div id="noLayout2"></div>
</div>
</div>
</div>
<script>
let outer = document.querySelector("#outer");
let intermediate = document.querySelector("#intermediate");
let target = document.querySelector("#target");
let padding = 5;
function cleanup() {
outer.className = "";
intermediate.className = "";
target.className = "";
document.body.offsetTop;
}
test( t => {
t.add_cleanup(cleanup);
outer.classList.add("abs-container");
target.classList.add("abs");
assert_equals(target.offsetHeight, outer.offsetHeight);
intermediate.classList.add("abs-container");
assert_equals(target.offsetHeight, intermediate.offsetHeight);
}, "abs containing block moves from outer to intermediate" );
test( t => {
t.add_cleanup(cleanup);
target.classList.add("abs");
intermediate.classList.add("abs-container");
assert_equals(target.offsetHeight, intermediate.offsetHeight);
outer.classList.add("abs-container");
assert_equals(target.offsetHeight, intermediate.offsetHeight);
intermediate.classList.remove("abs-container");
assert_equals(target.offsetHeight, outer.offsetHeight);
}, "abs containing block moves from intermediate to outer" );
test( t => {
t.add_cleanup(cleanup);
target.classList.add("abs");
outer.classList.add("abs-container");
assert_equals(target.offsetHeight, outer.offsetHeight);
target.classList.remove("abs");
assert_equals(target.offsetWidth, intermediate.offsetWidth - 4 * padding);
}, "target is no longer absolute");
test( t => {
t.add_cleanup(cleanup);
outer.classList.add("abs-container");
assert_equals(target.offsetWidth, intermediate.offsetWidth - 4 * padding);
target.classList.add("abs");
assert_equals(target.offsetHeight, outer.offsetHeight);
}, "target becomes absolute");
// Repeat same tests with fixed
test( t => {
t.add_cleanup(cleanup);
outer.classList.add("fixed-container");
target.classList.add("fixed");
assert_equals(target.offsetHeight, outer.offsetHeight);
intermediate.classList.add("fixed-container");
assert_equals(target.offsetHeight, intermediate.offsetHeight);
}, "fixed containing block moves from outer to intermediate" );
test( t => {
t.add_cleanup(cleanup);
target.classList.add("fixed");
intermediate.classList.add("fixed-container");
assert_equals(target.offsetHeight, intermediate.offsetHeight);
outer.classList.add("fixed-container");
assert_equals(target.offsetHeight, intermediate.offsetHeight);
intermediate.classList.remove("fixed-container");
assert_equals(target.offsetHeight, outer.offsetHeight);
}, "fixed containing block moves from intermediate to outer" );
test( t => {
t.add_cleanup(cleanup);
target.classList.add("fixed");
outer.classList.add("fixed-container");
assert_equals(target.offsetHeight, outer.offsetHeight);
target.classList.remove("fixed");
assert_equals(target.offsetWidth, intermediate.offsetWidth - 4 * padding);
}, "target is no longer fixed");
test( t => {
t.add_cleanup(cleanup);
outer.classList.add("fixed-container");
assert_equals(target.offsetWidth, intermediate.offsetWidth - 4 * padding);
target.classList.add("fixed");
assert_equals(target.offsetHeight, outer.offsetHeight);
}, "target becomes fixed");
</script>

View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<title>CSS Values and Units Test: rgba() function syntax (complex)</title>
<link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
<link rel="help" href="https://www.w3.org/TR/css-color-3/#rgba-color">
<link rel="help" href="https://www.w3.org/TR/css-color-4/#rgb-functions">
<link rel="help" href="https://www.w3.org/TR/css-values-4/#combine-integers">
<meta name="flags" content="">
<meta name="assert" content="This test checks the syntax allowed by rgba() function. White space characters, instead of commas, are allowed between numerical values. <alpha-value> can be omitted for rgba() function, in which case it must defaults to 100%. Finally, rgba() function can take real numbers but their computed values will be rounded to the nearest integer, with values halfway between adjacent integers rounded towards positive infinity.">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<div id="target"></div>
<script>
function startTesting()
{
var targetElement = document.getElementById("target");
function compareValue(property_name, calcValue, expectedValue, description)
{
test(function()
{
targetElement.style.setProperty(property_name, calcValue);
var computedCalcValue = getComputedStyle(targetElement)[property_name];
targetElement.style.setProperty(property_name, expectedValue);
var computedExpectedValue = getComputedStyle(targetElement)[property_name];
assert_equals(computedCalcValue, computedExpectedValue);
}, description);
}
compareValue("background-color", "rgba(0.4 127.5 0.3)", "rgb(0, 128, 0)", "testing background-color: rgba(0.4 127.5 0.3)");
}
startTesting();
</script>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<head>
<title>Custom Elements: CEReactions on HTMLBaseElement interface</title>
<link rel="author" title="Intel" href="http://www.intel.com">
<meta name="assert" content="href, target of HTMLBaseElement interface must have CEReactions">
<meta name="help" content="https://html.spec.whatwg.org/#the-base-element">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/custom-elements-helpers.js"></script>
<script src="./resources/reactions.js"></script>
</head>
<script>
function getParentElement() {
return document.head;
}
testReflectAttributeWithParentNode('href', 'href', '/', 'http://example.com/', 'href on HTMLBaseElement', 'base', getParentElement, HTMLBaseElement);
testReflectAttributeWithParentNode('target', 'target', '_blank', '_self', 'target on HTMLBaseElement', 'base', getParentElement, HTMLBaseElement);
</script>

View file

@ -10,55 +10,23 @@
<body>
<script>
function getParentElement(parentElementName) {
let parentElement = document.createElement(parentElementName);
document.body.appendChild(parentElement);
return parentElement;
}
testReflectAttributeWithContentValues('autofocus', 'autofocus', true, 'true', false, 'false', 'autofocus on HTMLButtonElement', 'button', HTMLButtonElement);
testReflectAttributeWithContentValues('disabled', 'disabled', true, 'true', false, 'false', 'disabled on HTMLButtonElement', 'button', HTMLButtonElement);
testReflectAttribute('name', 'name', 'intel', 'intel1', 'name on HTMLButtonElement', 'button', HTMLButtonElement);
testReflectAttribute('value', 'value', 'HTML', 'CSS', 'value on HTMLButtonElement', 'button', HTMLButtonElement);
testReflectAttrWithParentNode('type', 'type', 'submit', 'submit', 'reset', 'reset', 'type on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
testReflectAttributeWithParentNode('type', 'type', 'submit', 'reset', 'type on HTMLButtonElement', 'button', () => getParentElement('form'), HTMLButtonElement);
testReflectAttrWithDepAttr('formAction', 'formaction', 'type', 'intel.asp', 'intel1.asp', 'submit', 'formAction on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
testReflectAttrWithDepAttr('formEnctype', 'formenctype', 'type', 'text/plain', 'multipart/form-data', 'submit', 'formEnctype on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
testReflectAttrWithDepAttr('formMethod', 'formmethod', 'type', 'get', 'post', 'submit', 'formMethod on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
testReflectAttrWithContentValuesAndDepAttr('formNoValidate', 'formnovalidate', 'type', true, 'true', false, 'false', 'submit', 'formNoValidate on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
testReflectAttrWithDepAttr('formTarget', 'formtarget', 'type', '_blank', '_self', 'submit', 'formTarget on HTMLButtonElement', 'button', 'form', HTMLButtonElement);
//In parent node, sub node's observeAttribute can enqueue by changing attribute value
//Test reflect attribute with content values and parent node
function testReflectAttrWithParentNode(jsAtName, coAtName, jsAtValue1, coAtValue1, jsAtValue2, coAtValue2, name, elementName, pElementName, interfaceName) {
var parentElement = document.createElement(pElementName);
document.body.appendChild(parentElement);
test(() => {
var element = define_build_in_custom_element([coAtName], interfaceName, elementName);
var instance = document.createElement(elementName, { is: element.name });
assert_array_equals(element.takeLog().types(), ['constructed']);
parentElement.appendChild(instance);
assert_array_equals(element.takeLog().types(), ['connected']);
instance[jsAtName] = jsAtValue1;
var logEntries = element.takeLog();
assert_array_equals(logEntries.types(), ['attributeChanged']);
assert_attribute_log_entry(logEntries.last(), { name: coAtName, oldValue: null, newValue: coAtValue1, namespace: null });
}, name + ' must enqueue an attributeChanged reaction when adding a new attribute');
test(() => {
var element = define_build_in_custom_element([coAtName], interfaceName, elementName);
var instance = document.createElement(elementName, { is: element.name });
parentElement.appendChild(instance);
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
instance[jsAtName] = jsAtValue1;
assert_array_equals(element.takeLog().types(), ['attributeChanged']);
instance[jsAtName] = jsAtValue2;
var logEntries = element.takeLog();
assert_array_equals(logEntries.types(), ['attributeChanged']);
assert_attribute_log_entry(logEntries.last(), { name: coAtName, oldValue: coAtValue1, newValue: coAtValue2, namespace: null });
}, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
parentElement.parentNode.removeChild(parentElement);
}
//In parent node, sub node's observeAttribute which depends another attribute can enqueue by changing attribute value
//Test reflect attribute with content values and dependent attribute
function testReflectAttrWithContentValuesAndDepAttr(jsAtName, coAtName, deAtName, jsAtValue1, coAtValue1, jsAtValue2, coAtValue2, deAtValue, name, elementName, pElementName, interfaceName) {

View file

@ -168,6 +168,41 @@ function testReflectBooleanAttribute(jsAttributeName, contentAttributeName, name
testReflectAttributeWithContentValues(jsAttributeName, contentAttributeName, true, '', false, null, name, elementName, interfaceName);
}
function testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, validValue1, contentValue1, validValue2, contentValue2, name, elementName, getParentElement, interfaceName) {
let parentElement = getParentElement();
test(() => {
let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName);
let instance = document.createElement(elementName, { is: element.name });
assert_array_equals(element.takeLog().types(), ['constructed']);
parentElement.appendChild(instance);
assert_array_equals(element.takeLog().types(), ['connected']);
instance[jsAttributeName] = validValue1;
let logEntries = element.takeLog();
assert_array_equals(logEntries.types(), ['attributeChanged']);
assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: null, newValue: contentValue1, namespace: null });
}, name + ' must enqueue an attributeChanged reaction when adding a new attribute');
test(() => {
let element = define_build_in_custom_element([contentAttributeName], interfaceName, elementName);
let instance = document.createElement(elementName, { is: element.name });
parentElement.appendChild(instance);
assert_array_equals(element.takeLog().types(), ['constructed', 'connected']);
instance[jsAttributeName] = validValue1;
assert_array_equals(element.takeLog().types(), ['attributeChanged']);
instance[jsAttributeName] = validValue2;
let logEntries = element.takeLog();
assert_array_equals(logEntries.types(), ['attributeChanged']);
assert_attribute_log_entry(logEntries.last(), { name: contentAttributeName, oldValue: contentValue1, newValue: contentValue2, namespace: null });
}, name + ' must enqueue an attributeChanged reaction when replacing an existing attribute');
}
function testReflectAttributeWithParentNode(jsAttributeName, contentAttributeName, validValue1, validValue2, name, elementName, getParentElement, interfaceName) {
testReflectAttributeWithContentValuesAndParentNode(jsAttributeName, contentAttributeName, validValue1, validValue1, validValue2, validValue2, name, elementName, getParentElement, interfaceName);
}
function testAttributeAdder(testFunction, name) {
test(function () {
var element = define_new_custom_element(['id']);

View file

@ -9,12 +9,23 @@
<body>
<h1>domparsing_XMLSerializer_serializeToString</h1>
<script>
const XMLNS_URI = 'http://www.w3.org/2000/xmlns/';
function createXmlDoc(){
var input = '<?xml version="1.0" encoding="UTF-8"?><root><child1>value1</child1></root>';
var parser = new DOMParser();
return parser.parseFromString(input, 'text/xml');
}
// Returns the root element.
function parse(xmlString) {
return (new DOMParser()).parseFromString(xmlString, 'text/xml').documentElement;
}
function serialize(node) {
return (new XMLSerializer()).serializeToString(node);
}
test(function() {
var serializer = new XMLSerializer();
var root = createXmlDoc().documentElement;
@ -40,6 +51,78 @@ test(function() {
assert_equals(xmlString, '<root xmlns="urn:bar"><outer xmlns=""><inner>value1</inner></outer></root>');
}, 'Check if there is no redundant empty namespace declaration.');
test(function() {
const root = parse('<root xmlns="uri1"/>');
const child = root.ownerDocument.createElement('child');
child.setAttributeNS(XMLNS_URI, 'xmlns', 'FAIL1');
root.appendChild(child);
const child2 = root.ownerDocument.createElementNS('uri2', 'child2');
child2.setAttributeNS(XMLNS_URI, 'xmlns', 'FAIL2');
root.appendChild(child2);
const child3 = root.ownerDocument.createElementNS('uri1', 'child3');
child3.setAttributeNS(XMLNS_URI, 'xmlns', 'FAIL3');
root.appendChild(child3);
const child4 = root.ownerDocument.createElementNS('uri4', 'child4');
child4.setAttributeNS(XMLNS_URI, 'xmlns', 'uri4');
root.appendChild(child4);
const child5 = root.ownerDocument.createElement('child5');
child5.setAttributeNS(XMLNS_URI, 'xmlns', '');
root.appendChild(child5);
assert_equals(serialize(root), '<root xmlns="uri1"><child xmlns=""/><child2 xmlns="uri2"/><child3/><child4 xmlns="uri4"/><child5 xmlns=""/></root>');
}, 'Check if inconsistent xmlns="..." is dropped.');
test(function() {
let root = parse('<r xmlns:xx="uri"></r>');
root.setAttributeNS('uri', 'name', 'v');
assert_equals(serialize(root), '<r xmlns:xx="uri" xx:name="v"/>');
let root2 = parse('<r xmlns:xx="uri"><b/></r>');
let child = root2.firstChild;
child.setAttributeNS('uri', 'name', 'v');
assert_equals(serialize(root2), '<r xmlns:xx="uri"><b xx:name="v"/></r>');
let root3 = parse('<r xmlns:x0="uri" xmlns:x2="uri"><b xmlns:x1="uri"/></r>');
let child3 = root3.firstChild;
child3.setAttributeNS('uri', 'name', 'v');
assert_equals(serialize(root3),
'<r xmlns:x0="uri" xmlns:x2="uri"><b xmlns:x1="uri" x1:name="v"/></r>',
'Should choose the nearest prefix');
}, 'Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix');
test(function() {
let root = parse('<el1 xmlns:p="u1" xmlns:q="u1"><el2 xmlns:q="u2"/></el1>');
root.firstChild.setAttributeNS('u1', 'name', 'v');
assert_equals(serialize(root),
'<el1 xmlns:p="u1" xmlns:q="u1"><el2 xmlns:q="u2" q:name="v"/></el1>');
// Maybe this is a specification error.
}, 'Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix even if the prefix is assigned to another namespace.');
test(function() {
let root = parse('<r xmlns:xx="uri"></r>');
root.setAttributeNS('uri', 'p:name', 'v');
assert_equals(serialize(root), '<r xmlns:xx="uri" xx:name="v"/>');
let root2 = parse('<r xmlns:xx="uri"><b/></r>');
let child = root2.firstChild;
child.setAttributeNS('uri', 'p:name', 'value');
assert_equals(serialize(root2),
'<r xmlns:xx="uri"><b xx:name="value"/></r>');
}, 'Check if the prefix of an attribute is replaced with another existing prefix mapped to the same namespace URI.');
test(function() {
let root = parse('<r xmlns:xx="uri"></r>');
root.setAttributeNS('uri2', 'p:name', 'value');
assert_equals(serialize(root),
'<r xmlns:xx="uri" xmlns:ns1="uri2" ns1:name="value"/>');
}, 'Check if the prefix of an attribute is NOT preserved in a case where neither its prefix nor its namespace URI is not already used.');
test(function() {
let root = parse('<r xmlns:xx="uri"></r>');
root.setAttributeNS('uri2', 'xx:name', 'value');
assert_equals(serialize(root),
'<r xmlns:xx="uri" xmlns:ns1="uri2" ns1:name="value"/>');
}, 'Check if the prefix of an attribute is replaced with a generated one in a case where the prefix is already mapped to a different namespace URI.');
test(function() {
var serializer = new XMLSerializer();
var parser = new DOMParser();
@ -91,6 +174,16 @@ test(function() {
assert_equals(xmlString, '<root xmlns:ns2="uri2"><child xmlns:ns1="uri1" xmlns:ns1="uri3" ns1:attr1="value1"/></root>');
}, 'Check if "ns1" is generated even if the element already has xmlns:ns1.');
test(function() {
const root = (new Document()).createElement('root');
root.setAttributeNS('http://www.w3.org/1999/xlink', 'href', 'v');
assert_equals(serialize(root), '<root xmlns:ns1="http://www.w3.org/1999/xlink" ns1:href="v"/>');
const root2 = (new Document()).createElement('root');
root2.setAttributeNS('http://www.w3.org/1999/xlink', 'xl:type', 'v');
assert_equals(serialize(root2), '<root xmlns:xl="http://www.w3.org/1999/xlink" xl:type="v"/>');
}, 'Check if no special handling for XLink namespace unlike HTML serializer.');
</script>
</body>
</html>

View file

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Element Timing: observe image inside SVG</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/element-timing-helpers.js"></script>
<script>
let beforeRender;
async_test(function (t) {
const observer = new PerformanceObserver(
t.step_func_done(function(entryList) {
assert_equals(entryList.getEntries().length, 1);
const entry = entryList.getEntries()[0];
checkElement(entry, 'my_svg', beforeRender);
// Assume viewport has size at least 200, so the element is fully visible.
checkRect(entry, [0, 200, 0, 200]);
})
);
observer.observe({entryTypes: ['element']});
beforeRender = performance.now();
}, "Able to observe svg image.");
</script>
<style>
body {
margin: 0;
}
</style>
<svg>
<image href='resources/circle.svg' elementtiming='my_svg'/>
</svg>

View file

@ -13,7 +13,7 @@
let e = document.createElement('embed');
e.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"embed", "site":"same-origin", "user":"?F"};
let expected = {"dest":"embed", "site":"same-origin", "user":"?F", "mode":"no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -32,7 +32,7 @@
let e = document.createElement('embed');
e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"embed", "site":"same-site", "user":"?F"};
let expected = {"dest":"embed", "site":"same-site", "user":"?F", "mode":"no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -51,7 +51,7 @@
let e = document.createElement('embed');
e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"embed", "site":"cross-site", "user":"?F"};
let expected = {"dest":"embed", "site":"cross-site", "user":"?F", "mode":"no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))

View file

@ -3,6 +3,7 @@
<script src=/resources/testharnessreport.js></script>
<script src=/fetch/sec-metadata/resources/helper.js></script>
<script>
// Site
promise_test(t => {
return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py")
.then(r => r.json())
@ -10,7 +11,8 @@
assert_header_equals(j, {
"dest": "empty",
"site": "same-origin",
"user":"?F"
"user": "?F",
"mode": "cors",
});
});
}, "Same-origin fetch");
@ -22,7 +24,8 @@
assert_header_equals(j, {
"dest": "empty",
"site": "same-site",
"user":"?F"
"user": "?F",
"mode": "cors",
});
});
}, "Same-site fetch");
@ -34,8 +37,49 @@
assert_header_equals(j, {
"dest": "empty",
"site": "cross-site",
"user":"?F"
"user": "?F",
"mode": "cors",
});
});
}, "Cross-site fetch");
// Mode
promise_test(t => {
return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py", {mode: "same-origin"})
.then(r => r.json())
.then(j => {
assert_header_equals(j, {
"dest": "empty",
"site": "same-origin",
"user": "?F",
"mode": "same-origin",
});
});
}, "Same-origin mode");
promise_test(t => {
return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py", {mode: "cors"})
.then(r => r.json())
.then(j => {
assert_header_equals(j, {
"dest": "empty",
"site": "same-origin",
"user": "?F",
"mode": "cors",
});
});
}, "CORS mode");
promise_test(t => {
return fetch("https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-json.py", {mode: "no-cors"})
.then(r => r.json())
.then(j => {
assert_header_equals(j, {
"dest": "empty",
"site": "same-origin",
"user": "?F",
"mode": "no-cors",
});
});
}, "no-CORS mode");
</script>

View file

@ -46,7 +46,7 @@
promise_test(t => {
return new Promise((resolve, reject) => {
let key = "font-same-origin";
let expected = {"dest":"font", "site":"same-origin", "user":"?F"};
let expected = {"dest":"font", "site":"same-origin", "user":"?F", "mode": "cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -58,7 +58,7 @@
promise_test(t => {
return new Promise((resolve, reject) => {
let key = "font-same-site";
let expected = {"dest":"font", "site":"same-site", "user":"?F"};
let expected = {"dest":"font", "site":"same-site", "user":"?F", "mode": "cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -70,7 +70,7 @@
promise_test(t => {
return new Promise((resolve, reject) => {
let key = "font-cross-site";
let expected = {"dest":"font", "site":"cross-site", "user":"?F"};
let expected = {"dest":"font", "site":"cross-site", "user":"?F", "mode": "cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))

View file

@ -14,7 +14,8 @@
assert_header_equals(e.data, {
"dest": "nested-document",
"site": "same-origin",
"user":"?F"
"user": "?F",
"mode": "navigate"
});
t.done();
}));
@ -32,7 +33,8 @@
assert_header_equals(e.data, {
"dest": "nested-document",
"site": "same-site",
"user": "?F"
"user": "?F",
"mode": "navigate"
});
t.done();
}));
@ -50,7 +52,8 @@
assert_header_equals(e.data, {
"dest": "nested-document",
"site": "cross-site",
"user": "?F"
"user": "?F",
"mode": "navigate"
});
t.done();
}));

View file

@ -14,7 +14,8 @@
assert_header_equals(e.data, {
"dest": "",
"site": "",
"user": ""
"user": "",
"mode": "",
});
t.done();
}));
@ -32,7 +33,8 @@
assert_header_equals(e.data, {
"dest": "",
"site": "",
"user": ""
"user": "",
"mode": "",
});
t.done();
}));
@ -50,7 +52,8 @@
assert_header_equals(e.data, {
"dest": "",
"site": "",
"user": ""
"user": "",
"mode": "",
});
t.done();
}));

View file

@ -21,7 +21,8 @@
assert_header_equals(got, {
"dest": "image",
"site": "same-origin",
"user": "?F"
"user": "?F",
"mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
});
}),
[],
@ -42,7 +43,8 @@
assert_header_equals(got, {
"dest": "image",
"site": "same-site",
"user": "?F"
"user": "?F",
"mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
});
}),
[],
@ -63,7 +65,8 @@
assert_header_equals(got, {
"dest": "image",
"site": "cross-site",
"user": "?F"
"user": "?F",
"mode": "cors", // Because `loadImageInWindow` tacks on `crossorigin`
});
}),
[],

View file

@ -13,7 +13,7 @@
let e = document.createElement('object');
e.data = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"object", "site":"same-origin", "user":"?F"};
let expected = {"dest":"object", "site":"same-origin", "user":"?F", "mode":"no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -32,7 +32,7 @@
let e = document.createElement('object');
e.data = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"object", "site":"same-site", "user":"?F"};
let expected = {"dest":"object", "site":"same-site", "user":"?F", "mode":"no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -51,7 +51,7 @@
let e = document.createElement('object');
e.data = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"object", "site":"cross-site", "user":"?F"};
let expected = {"dest":"object", "site":"cross-site", "user":"?F", "mode":"no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))

View file

@ -12,7 +12,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
@ -38,7 +38,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
@ -64,7 +64,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())

View file

@ -14,7 +14,7 @@ promise_test(t => {
e.src = "https://{{host}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// same-origin
"https://{{hosts[alt][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// cross-site
"https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;// same-origin
let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)

View file

@ -14,7 +14,7 @@ promise_test(t => {
e.src = "https://{{host}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// same-origin
"https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=" +// same-site
"https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;// same-origin
let expected = {"dest":"image", "site":"same-site", "user":"?F"};
let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)

View file

@ -12,7 +12,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"same-origin", "user":"?F"};
let expected = {"dest":"image", "site":"same-origin", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@ -39,7 +39,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "/xhr/resources/redirect.py?location=https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"same-site", "user":"?F"};
let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@ -66,7 +66,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "/xhr/resources/redirect.py?location=https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)

View file

@ -12,7 +12,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"same-site", "user":"?F"};
let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@ -39,7 +39,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"same-site", "user":"?F"};
let expected = {"dest":"image", "site":"same-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
@ -66,7 +66,7 @@ promise_test(t => {
let e = document.createElement('img');
e.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/xhr/resources/redirect.py?location=https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
let expected = {"dest":"image", "site":"cross-site", "user":"?F"};
let expected = {"dest":"image", "site":"cross-site", "user":"?F", "mode": "no-cors"};
e.onload = e => {
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)

View file

@ -22,9 +22,9 @@
document.addEventListener("securitypolicyviolation", (e) => {
counter++;
if (counter == 3) {
generate_test({"dest":"report", "site":"same-origin", "user":"?F"}, "same-origin");
generate_test({"dest":"report", "site":"same-site", "user":"?F"}, "same-site");
generate_test({"dest":"report", "site":"cross-site", "user":"?F"}, "cross-site");
generate_test({"dest":"report", "site":"same-origin", "user":"?F", "mode": "no-cors"}, "same-origin");
generate_test({"dest":"report", "site":"same-site", "user":"?F", "mode": "no-cors"}, "same-site");
generate_test({"dest":"report", "site":"cross-site", "user":"?F", "mode": "no-cors"}, "cross-site");
}
});
}, "Initialization.");

View file

@ -4,8 +4,7 @@ function assert_header_equals(value, expected) {
value = JSON.parse(value);
}
assert_equals(value.dest, expected.dest, "dest");
// Mode is commented out as no test cases have been filled out yet
// assert_equals(value.mode, expected.mode, "mode");
assert_equals(value.mode, expected.mode, "mode");
assert_equals(value.site, expected.site, "site");
assert_equals(value.user, expected.user, "user");
}

View file

@ -12,7 +12,8 @@
assert_header_equals(header, {
"dest": "script",
"site": "same-origin",
"user":"?F"
"user": "?F",
"mode": "no-cors",
});
}, "Same-origin script");
</script>
@ -26,7 +27,8 @@
assert_header_equals(header, {
"dest": "script",
"site": "same-site",
"user":"?F"
"user": "?F",
"mode": "no-cors",
});
}, "Same-site script");
</script>
@ -40,7 +42,23 @@
assert_header_equals(header, {
"dest": "script",
"site": "cross-site",
"user":"?F"
"user": "?F",
"mode": "no-cors",
});
}, "Cross-site script");
</script>
<!-- Same-origin script, CORS mode -->
<script src="https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/echo-as-script.py" crossorigin="anonymous"></script>
<script>
test(t => {
t.add_cleanup(_ => { header = null; });
assert_header_equals(header, {
"dest": "script",
"site": "same-origin",
"user": "?F",
"mode": "cors",
});
}, "Same-origin CORS script");
</script>

View file

@ -12,7 +12,8 @@
assert_header_equals(header, {
"dest": "",
"site": "",
"user": ""
"user": "",
"mode": "",
});
}, "Non-secure same-origin script => No headers");
</script>
@ -26,7 +27,8 @@
assert_header_equals(header, {
"dest": "",
"site": "",
"user": ""
"user": "",
"mode": "",
});
}, "Non-secure same-site script => No headers");
</script>
@ -40,7 +42,8 @@
assert_header_equals(header, {
"dest": "",
"site": "",
"user": ""
"user": "",
"mode": "",
});
}, "Non-secure cross-site script => No headers");
</script>

View file

@ -35,7 +35,7 @@
promise_test(t => {
return new Promise((resolve, reject) => {
let key = "serviceworker-same-origin";
let expected = {"dest":"serviceworker", "site":"same-origin", "user":"?F"};
let expected = {"dest":"serviceworker", "site":"same-origin", "user":"?F", "mode": "same-origin"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))

View file

@ -26,7 +26,7 @@
promise_test(t => {
return new Promise((resolve, reject) => {
let key = "sharedworker-same-origin";
let expected = {"dest":"sharedworker", "site":"same-origin", "user":"?F"};
let expected = {"dest":"sharedworker", "site":"same-origin", "user":"?F", "mode": "same-origin"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())

View file

@ -14,7 +14,7 @@
e.rel = "stylesheet";
e.href = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"style", "site":"same-origin", "user":"?F"};
let expected = {"dest":"style", "site":"same-origin", "user":"?F", "mode": "no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -34,7 +34,7 @@
e.rel = "stylesheet";
e.href = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"style", "site":"same-site", "user":"?F"};
let expected = {"dest":"style", "site":"same-site", "user":"?F", "mode": "no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -54,7 +54,7 @@
e.rel = "stylesheet";
e.href = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.onload = e => {
let expected = {"dest":"style", "site":"cross-site", "user":"?F"};
let expected = {"dest":"style", "site":"cross-site", "user":"?F", "mode": "no-cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -65,6 +65,27 @@
document.body.appendChild(e);
})
}, "Cross-Site style");
promise_test(t => {
return new Promise((resolve, reject) => {
let key = "style-same-origin";
let e = document.createElement('link');
e.rel = "stylesheet";
e.href = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=" + key;
e.crossOrigin = "anonymous";
e.onload = e => {
let expected = {"dest":"style", "site":"same-origin", "user":"?F", "mode": "cors"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
.then(_ => resolve())
.catch(e => reject(e));
};
document.body.appendChild(e);
})
}, "Same-Origin, cors style");
</script>
</html>

View file

@ -29,7 +29,12 @@
let el = createTrack();
el.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-origin";
el.onload = t.step_func(_ => {
expected = {"dest":"track", "site":"same-origin", "user":"?F"};
expected = {
"dest": "track",
"site": "same-origin",
"user": "?F",
"mode": "cors" // Because the `video` element has `crossorigin`
};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-origin")
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -46,7 +51,12 @@
let el = createTrack();
el.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-site";
el.onload = t.step_func(_ => {
expected = {"dest":"track", "site":"same-site", "user":"?F"};
expected = {
"dest": "track",
"site": "same-site",
"user": "?F",
"mode": "cors" // Because the `video` element has `crossorigin`
};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-site")
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -65,7 +75,12 @@
let el = createTrack();
el.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-cross-site";
el.onload = t.step_func(_ => {
expected = {"dest":"track", "site":"cross-site", "user":"?F"};
expected = {
"dest": "track",
"site": "cross-site",
"user": "?F",
"mode": "cors" // Because the `video` element has `crossorigin`
};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-cross-site")
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
@ -76,4 +91,30 @@
document.body.appendChild(video);
});
}, "Cross-Site track");
promise_test(t => {
return new Promise((resolve, reject) => {
let video = createVideoElement();
// Unset `crossorigin` to change the CORS mode:
video.crossOrigin = undefined;
let el = createTrack();
el.src = "https://{{host}}:{{ports[https][0]}}/fetch/sec-metadata/resources/record-header.py?file=track-same-origin";
el.onload = t.step_func(_ => {
expected = {
"dest":"track",
"site":"same-origin",
"user":"?F",
"mode": "same-origin"
};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=track-same-origin")
.then(response => response.text())
.then(text => assert_header_equals(text, expected))
.then(_ => resolve());
});
video.appendChild(el);
document.body.appendChild(video);
});
}, "Same-Origin, CORS track");
</script>

View file

@ -17,7 +17,8 @@
assert_header_equals(e.data, {
"dest": "document",
"site": "same-origin",
"user":"?F"
"user": "?F",
"mode": "navigate",
});
t.done();
}));
@ -33,7 +34,8 @@
assert_header_equals(e.data, {
"dest": "document",
"site": "same-site",
"user":"?F"
"user": "?F",
"mode": "navigate",
});
t.done();
}));
@ -49,7 +51,8 @@
assert_header_equals(e.data, {
"dest": "document",
"site": "cross-site",
"user":"?F"
"user": "?F",
"mode": "navigate",
});
t.done();
}));
@ -68,7 +71,8 @@
assert_header_equals(e.data, {
"dest": "document",
"site": "same-origin",
"user": "?T"
"user": "?T",
"mode": "navigate",
});
t.done();
}));
@ -89,7 +93,8 @@
assert_header_equals(e.data, {
"dest": "document",
"site": "same-site",
"user": "?T"
"user": "?T",
"mode": "navigate",
});
t.done();
}));
@ -110,7 +115,8 @@
assert_header_equals(e.data, {
"dest": "document",
"site": "cross-site",
"user": "?T"
"user": "?T",
"mode": "navigate",
});
t.done();
}));

View file

@ -10,7 +10,7 @@
let key = "worker-same-origin";
let w = new Worker("/fetch/sec-metadata/resources/record-header.py?file=" + key);
w.onmessage = e => {
let expected = {"dest":"worker", "site":"same-origin", "user":"?F"};
let expected = {"dest":"worker", "site":"same-origin", "user":"?F", "mode": "same-origin"};
fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=" + key)
.then(response => response.text())
.then(text => assert_header_equals(text, expected))

View file

@ -12,21 +12,21 @@
return;
promise_test(t => {
let expected = {"dest":"xslt", "site":"same-origin", "user":"?F"};
let expected = {"dest":"xslt", "site":"same-origin", "user":"?F", "mode": "same-origin"};
return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-same-origin")
.then(response => response.text())
.then(text => assert_header_equals(text, expected));
}, "Same-Origin xslt");
promise_test(t => {
let expected = {"dest":"xslt", "site":"same-site", "user":"?F"};
let expected = {"dest":"xslt", "site":"same-site", "user":"?F", "mode": "no-cors"};
return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-same-site")
.then(response => response.text())
.then(text => assert_header_equals(text, expected));
}, "Same-site xslt");
promise_test(t => {
let expected = {"dest":"xslt", "site":"cross-site", "user":"?F"};
let expected = {"dest":"xslt", "site":"cross-site", "user":"?F", "mode": "no-cors"};
return fetch("/fetch/sec-metadata/resources/record-header.py?retrieve=true&file=xslt-cross-site")
.then(response => response.text())
.then(text => assert_header_equals(text, expected));

View file

@ -0,0 +1,193 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/document_domain_frame.sub.js"></script>
<body>
<script>
promise_test(async (t) => {
let frame1 = await createFrame(t, "control1-1", "{{domains[www1]}}");
let frame2 = await createFrame(t, "control1-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "control1-2" });
assert_equals(result.data, "omg!");
}, "Access allowed if same-origin with no 'document.domain' modification. (Sanity check)");
promise_test(async (t) => {
let frame1 = await createFrame(t, "control2-1", "{{domains[www1]}}");
let frame2 = await createFrame(t, "control2-2", "{{domains[www2]}}");
let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "control2-2" });
assert_equals(result.data, "SecurityError");
}, "Access not allowed if different-origin with no 'document.domain' modification. (Sanity check)");
promise_test(async (t) => {
let frame1 = await createFrame(t, "one-set-one-not-1", "{{domains[www1]}}");
let frame2 = await createFrame(t, "one-set-one-not-2", "{{domains[www1]}}");
await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
let result = await postMessageToFrame(frame1, { 'poke-at-sibling': "one-set-one-not-2" });
assert_equals(result.data, "SecurityError");
result = await postMessageToFrame(frame2, { 'poke-at-sibling': "one-set-one-not-1" });
assert_equals(result.data, "SecurityError");
}, "Access disallowed if same-origin but only one sets document.domain.");
promise_test(async (t) => {
var frame1 = await createFrame(t, "both-set-to-existing-1", "{{domains[www1]}}");
var frame2 = await createFrame(t, "both-set-to-existing-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, { 'poke-at-sibling': "both-set-to-existing-2" });
assert_equals(result.data, "omg!");
result = await postMessageToFrame(frame2, { 'poke-at-sibling': "both-set-to-existing-1" });
assert_equals(result.data, "omg!");
}, "Access allowed if same-origin and both set document.domain to existing value.");
promise_test(async (t) => {
var frame1 = await createFrame(t, "both-set-to-parent-1", "{{domains[www1]}}");
var frame2 = await createFrame(t, "both-set-to-parent-2", "{{domains[www2]}}");
let result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame2, { domain: "{{domains[]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, { 'poke-at-sibling': "both-set-to-parent-2" });
assert_equals(result.data, "omg!");
result = await postMessageToFrame(frame2, { 'poke-at-sibling': "both-set-to-parent-1" });
assert_equals(result.data, "omg!");
}, "Access allowed if different-origin but both set document.domain to parent domain.");
promise_test(async (t) => {
var frame1 = await createFrame(t, "allow-then-revoke-1", "{{domains[www1]}}");
var frame2 = await createFrame(t, "allow-then-revoke-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame2, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, { 'poke-at-sibling': "allow-then-revoke-2" });
assert_equals(result.data, "omg!");
result = await postMessageToFrame(frame2, { 'poke-at-sibling': "allow-then-revoke-1" });
assert_equals(result.data, "omg!");
result = await postMessageToFrame(frame1, { domain: "{{domains[]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, { 'poke-at-sibling': "allow-then-revoke-2" });
assert_equals(result.data, "SecurityError");
result = await postMessageToFrame(frame2, { 'poke-at-sibling': "allow-then-revoke-1" });
assert_equals(result.data, "SecurityError");
}, "Access disallowed again if same-origin, both set document-domain to existing value, then one sets to parent.");
promise_test(async (t) => {
let frame1 = await createFrame(t, "revoke-Window-1", "{{domains[www1]}}");
let frame2 = await createFrame(t, "revoke-Window-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { cache: ["parent", "revoke-Window-2"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 1");
result = await postMessageToFrame(frame2, { cache: ["parent", "revoke-Window-1"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 1");
result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "SecurityError");
result = await postMessageToFrame(frame2, 'touch-cached');
assert_equals(result.data, "SecurityError");
}, "Access is revoked to Window object when we stop being same effective script origin due to document.domain.");
promise_test(async (t) => {
let frame1 = await createFrame(t, "revoke-Location-1", "{{hosts[][www1]}}");
let frame2 = await createFrame(t, "revoke-Location-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { cache: ["parent", "revoke-Location-2", "location"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 3");
result = await postMessageToFrame(frame2, { cache: ["parent", "revoke-Location-1", "location"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 3");
result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "SecurityError");
result = await postMessageToFrame(frame2, 'touch-cached');
assert_equals(result.data, "SecurityError");
}, "Access is revoked to Location object when we stop being same effective script origin due to document.domain.");
promise_test(async (t) => {
let frame1 = await createFrame(t, "no-revoke-Document-1", "{{domains[www1]}}");
let frame2 = await createFrame(t, "no-revoke-Document-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { cache: ["parent", "no-revoke-Document-2", "document"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 4");
result = await postMessageToFrame(frame2, { cache: ["parent", "no-revoke-Document-1", "document"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 4");
result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 4");
result = await postMessageToFrame(frame2, 'touch-cached');
assert_equals(result.data, "Reachable 4");
}, "Access is not revoked to Document object when we stop being same effective script origin due to document.domain.");
promise_test(async (t) => {
let frame1 = await createFrame(t, "no-revoke-object-1", "{{domains[www1]}}");
let frame2 = await createFrame(t, "no-revoke-object-2", "{{domains[www1]}}");
let result = await postMessageToFrame(frame1, { cache: ["parent", "no-revoke-object-2", "bar"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 2");
result = await postMessageToFrame(frame2, { cache: ["parent", "no-revoke-object-1", "bar"] });
assert_equals(result.data, "cached");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 2");
result = await postMessageToFrame(frame1, { domain: "{{domains[www1]}}" });
assert_equals(result.data, "Done");
result = await postMessageToFrame(frame1, 'touch-cached');
assert_equals(result.data, "Reachable 2");
result = await postMessageToFrame(frame2, 'touch-cached');
assert_equals(result.data, "Reachable 2");
}, "Access is not revoked to random object when we stop being same effective script origin due to document.domain.");
</script>

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<script>
let cache = window;
// "foo" needs to be a var so it's a property on the global.
var foo = 'Reachable 1';
// "bar" needs to be a var so it's a property on the global.
var bar = { foo: 'Reachable 2' };
location.foo = 'Reachable 3';
document.foo = 'Reachable 4';
window.addEventListener('message', e => {
if (e.data.domain !== undefined) {
try {
document.domain = e.data.domain;
e.ports[0].postMessage('Done');
} catch(error) {
e.ports[0].postMessage(error.name);
}
} else if (e.data['poke-at-sibling'] !== undefined) {
try {
var sekrit = parent[e.data['poke-at-sibling']].document.body.querySelector('#sekrit').value;
e.ports[0].postMessage(sekrit);
} catch(error) {
e.ports[0].postMessage(error.name);
}
} else if (e.data.cache != undefined) {
let path = e.data.cache;
try {
while (path.length != 0) {
cache = cache[path.shift()];
}
e.ports[0].postMessage('cached');
} catch (error) {
e.ports[0].postMessage(error.name);
}
} else if (e.data == 'touch-cached') {
try {
e.ports[0].postMessage(cache.foo);
} catch (error) {
e.ports[0].postMessage(error.name);
}
} else if (e.data == 'poke-at-parent') {
try {
var sekrit = window.parent.document.body.querySelector('#sekrit').value;
e.ports[0].postMessage(sekrit);
} catch(error) {
e.ports[0].postMessage(error.name);
}
}
});
window.parent.postMessage('Hi!', '*');
</script>
<input id="sekrit" value="omg!">

View file

@ -0,0 +1,65 @@
/**
* Utilities to be used with document_domain_frame.html.
*/
/**
* Send a message to the frame and resolve a promise when a response is received.
*
* Supported messages:
*
* 1) { domain: something }. Has the subframe try to set document.domain to the
* given value, and message back 'Done' if that succeeds or an error name if it
* fails.
*
* 2) 'poke-at-parent'. Has the subframe try to synchronously attempt to access
* the parent's DOM, read out a string value, and message it back to the parent.
* Again, sends back the error name if that fails.
*
* 3) { 'poke-at-sibling': name }. Has the subframe try to synchronously
* attempt to access the DOM of the sibling with the given name, read out a
* string value, and message it back to the parent.
*/
function postMessageToFrame(frame, message) {
return new Promise(resolve => {
var c = new MessageChannel();
c.port1.onmessage = e => {
resolve({ data: e.data, frame: frame })
};
frame.contentWindow.postMessage(message, '*', [c.port2]);
});
}
/**
* Create a frame that loads document_domain_frame.html and resolves a promise
* when the frame is loaded enough to be sending and receiving messages.
*
* If a "name" argument is provided, that name is used for the iframe, so
*
* If a "hostname" argument is provided, that hostname is used for the load, to
* allow testing details of the behavior when different sorts of hostnames are
* used.
*/
function createFrame(t, name, hostname) {
return new Promise(resolve => {
var i = document.createElement('iframe');
if (hostname) {
i.src = `//${hostname}:{{location[port]}}/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html`;
} else {
i.src = "support/document_domain_frame.html";
}
if (name) {
i.name = name;
}
var listener = m => {
if (m.source == i.contentWindow)
resolve(i);
}
window.addEventListener('message', listener);
t.add_cleanup(() => {
i.remove();
window.removeEventListener('message', listener);
});
document.body.appendChild(i);
});
}

View file

@ -0,0 +1,18 @@
test(() => {
const frame = document.createElement("iframe"),
name = "A",
name2 = "B";
frame.setAttribute("name", name);
document.body.append(frame);
const frameW = frame.contentWindow;
assert_equals(frameW.name, name);
frameW.name = name2;
assert_equals(frame.getAttribute("name"), name);
assert_equals(frameW.name, name2);
frame.remove();
assert_equals(frame.getAttribute("name"), name);
assert_equals(frameW.name, "");
frameW.name = name2;
assert_equals(frame.getAttribute("name"), name);
assert_equals(frameW.name, "");
}, "Window object's name IDL attribute");

View file

@ -13,8 +13,8 @@ test(function() {
var a = document.getElementById("test");
test(function() {
a.rel = "noreferrer"
assert_equals(a.rel, "noreferrer");
a.rel = "noreferrer";
assert_equals(a.getAttribute("rel"), "noreferrer");
}, "Test anchor's rel setter");
});
</script>

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(t => {
assert_false("getUserAgent" in navigator);
}, "navigator.getUserAgent() is not available in non-secure contexts.");
</script>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webrtc/dictionary-helper.js"></script>
<script>
test(t => {
assert_true("getUserAgent" in navigator);
}, "navigator.getUserAgent() is exposed.");
promise_test(t => {
return navigator.getUserAgent()
.then(ua => {
// TODO(web-platform-tests/wpt#9106): Use `idlharness.js` once it supports dictionaries.
assert_string_field(ua, "brand");
assert_string_field(ua, "version");
assert_string_field(ua, "platform");
assert_string_field(ua, "architecture");
assert_string_field(ua, "model");
});
}, "navigator.getUserAgent() returns a UserAgentMetadata object.");
</script>

View file

@ -0,0 +1 @@
Content-Type: text/javascript

View file

@ -0,0 +1 @@
log.push("relative:@std/blank");

View file

@ -0,0 +1 @@
log.push("relative:@std/none");

View file

@ -0,0 +1,9 @@
Tests for [Import Maps](https://github.com/WICG/import-maps).
Because the spec itself is still under development and there are ongoing spec
discussions, the tests are all tentative.
Also, some tests are based on Chromium's behavior which reflects an older
version of import maps spec ("package name maps" around May 2018), and have
dependency to Chromium's implementation (internals.resolveModuleSpecifier).
These dependencies should be removed, once the spec matures.

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// https://github.com/WICG/import-maps/blob/master/spec.md#when-import-maps-can-be-encountered
const t = async_test(
'After dynamic imports, import maps should fire error events');
const log = [];
// To ensure we are testing that the flag is cleared at the beginning of module
// script loading unconditionally, not at the end of loading or not at the
// first attempt to resolve a module specifier, trickle(d1) is used to ensure
// the following import map is added after module loading is triggered but
// before the first module script is parsed.
promise_test(() => import('../../resources/empty.js?pipe=trickle(d1)'),
"A dynamic import succeeds");
</script>
<script type="importmap" onload="t.assert_unreached('onload')" onerror="t.done()">
{
"imports": {
"../../resources/log.js?pipe=sub&name=A": "../../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script>
promise_test(() => {
return import("../../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
},
'After a dynamic import(), import maps are not effective');
</script>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// https://github.com/WICG/import-maps/blob/master/spec.md#when-import-maps-can-be-encountered
const t = async_test(
'After <script type="module"> import maps should fire error events');
const log = [];
</script>
<script type="module" src="../../resources/empty.js?pipe=trickle(d1)"></script>
<script type="importmap" onerror="t.done()">
{
"imports": {
"../../resources/log.js?pipe=sub&name=A": "../../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script>
promise_test(() => {
return import("../../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
},
'After <script type="module"> import maps are not effective');
</script>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// https://github.com/WICG/import-maps/blob/master/spec.md#when-import-maps-can-be-encountered
const t = async_test(
'After module worker creation, import maps should fire error events');
const log = [];
new Worker('../../resources/empty.js?pipe=trickle(d1)', {type: "module"});
</script>
<script type="importmap" onerror="t.done()">
{
"imports": {
"../../resources/log.js?pipe=sub&name=A": "../../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script>
promise_test(() => {
return import("../../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["A"]))
},
'After module worker creation import maps are not effective');
</script>

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
// "bare/..." (i.e. without leading "./") are bare specifiers
// (not relative paths).
//
// Discussions about notations for builtin modules are ongoing, e.g.
// https://github.com/tc39/proposal-javascript-standard-library/issues/12
// Currently the tests expects two notations are accepted.
// TODO: Once the discussions converge, update the tests.
const importMap = `
{
"imports": {
"bare/bare": "./resources/log.js?pipe=sub&name=bare",
"bare/cross-origin-bare": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-bare",
"bare/to-data": "data:text/javascript,log.push('dataURL')",
"bare/std-blank": "std:blank",
"bare/blank": "@std/blank",
"bare/std-none": "std:none",
"bare/none": "@std/none",
"bare/to-bare": "bare/bare"
}
}
`;
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Currently, Chromium's implementation resolves import maps as a part of
// specifier resolution, and thus failure in import map resolution causes
// a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
// below. https://crbug.com/928435
// Bare to HTTP(S).
"bare/bare":
[Result.URL, Result.URL, "log:bare", "log:bare"],
"bare/cross-origin-bare":
[Result.URL, Result.URL, "log:cross-origin-bare", "log:cross-origin-bare"],
// Bare to data:
"bare/to-data":
[Result.URL, Result.URL, "dataURL", "dataURL"],
// Bare to built-in.
"bare/std-blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"bare/blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"bare/std-none":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
"bare/none":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
// Bare to bare mapping is disabled.
"bare/to-bare":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
};
doTests(importMap, null, tests);
</script>
<body>

View file

@ -0,0 +1 @@
Content-Type: text/javascript

View file

@ -0,0 +1 @@
log.push("relative:bare/bare");

View file

@ -0,0 +1 @@
log.push("relative:bare/blank");

View file

@ -0,0 +1 @@
log.push("relative:bare/cross-origin-bare");

View file

@ -0,0 +1 @@
log.push("relative:bare/none");

View file

@ -0,0 +1 @@
log.push("relative:bare/std-blank");

View file

@ -0,0 +1 @@
log.push("relative:bare/std-none");

View file

@ -0,0 +1 @@
log.push("relative:bare/to-bare");

View file

@ -0,0 +1 @@
log.push("relative:bare/to-data");

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
const importMap = `
{
"imports": {
"./resources/log.js?pipe=sub&name=empty": [ "@std/" ],
"./resources/log.js?pipe=sub&name=empty-fallback": [
"@std/",
"./resources/log.js?pipe=sub&name=empty-fallback"
]
}
}
`;
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Discussions about notations are ongoing, e.g.
// https://github.com/tc39/proposal-javascript-standard-library/issues/12
// Currently the tests expects two notations are accepted.
// TODO: Once the discussions converge, update this and related tests.
"std:":
[Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
"@std/":
[Result.FETCH_ERROR, Result.PARSE_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
"./resources/log.js?pipe=sub&name=empty":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
"./resources/log.js?pipe=sub&name=empty-fallback":
[Result.URL, Result.URL, Result.URL, Result.URL],
};
doTests(importMap, null, tests);
</script>
<body>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Currently direct use of import: URLs are disabled.
"import:std:blank":
[Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
"import:@std/blank":
[Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
"import:std:none":
[Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
"import:@std/none":
[Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
};
doTests(null, null, tests);
</script>
<body>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Discussions about notations are ongoing, e.g.
// https://github.com/tc39/proposal-javascript-standard-library/issues/12
// Currently the tests expects two notations are accepted.
// TODO: Once the discussions converge, update this and related tests.
"std:blank":
[Result.BUILTIN, Result.FETCH_ERROR, Result.BUILTIN, Result.BUILTIN],
"@std/blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"std:none":
[Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR, Result.FETCH_ERROR],
"@std/none":
[Result.URL, Result.URL, Result.FETCH_ERROR, Result.FETCH_ERROR],
};
doTests(null, null, tests);
</script>
<body>

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
// "bare/..." (i.e. without leading "./") are bare specifiers
// (not relative paths).
//
// Discussions about notations for builtin modules are ongoing, e.g.
// https://github.com/tc39/proposal-javascript-standard-library/issues/12
// Currently the tests expects two notations are accepted.
// TODO: Once the discussions converge, update the tests.
const importMap = `
{
"imports": {
"bare": "./resources/log.js?pipe=sub&name=bare",
"data:text/javascript,log.push('data:foo')": "./resources/log.js?pipe=sub&name=foo",
"data:text/javascript,log.push('data:cross-origin-foo')": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-foo",
"data:text/javascript,log.push('data:to-data')": "data:text/javascript,log.push('dataURL')",
"data:text/javascript,log.push('data:std-blank')": "std:blank",
"data:text/javascript,log.push('data:blank')": "@std/blank",
"data:text/javascript,log.push('data:std-none')": "std:none",
"data:text/javascript,log.push('data:none')": "@std/none",
"data:text/javascript,log.push('data:to-bare')": "bare"
}
}
`;
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Currently, Chromium's implementation resolves import maps as a part of
// specifier resolution, and thus failure in import map resolution causes
// a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
// below. https://crbug.com/928435
// data: to HTTP(S).
"data:text/javascript,log.push('data:foo')":
[Result.URL, Result.URL, "log:foo", "log:foo"],
"data:text/javascript,log.push('data:cross-origin-foo')":
[Result.URL, Result.URL, "log:cross-origin-foo", "log:cross-origin-foo"],
// data: to data:
"data:text/javascript,log.push('data:to-data')":
[Result.URL, Result.URL, "dataURL", "dataURL"],
// data: to built-in.
"data:text/javascript,log.push('data:std-blank')":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"data:text/javascript,log.push('data:blank')":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"data:text/javascript,log.push('data:std-none')":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
"data:text/javascript,log.push('data:none')":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
// data: to bare mapping is disabled.
"data:text/javascript,log.push('data:to-bare')":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
};
doTests(importMap, null, tests);
</script>
<body>

View file

@ -0,0 +1,79 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
// Fallbacks from external URLs (such as HTTPS URLs) are
// blocked by ongoing spec discussions, for example
// https://github.com/WICG/import-maps/issues/76.
// https://crbug.com/928435
//
// This test, as well as Chromium's implementation, rejects broader range of
// fallbacks (not only those from HTTPS), to avoid potential spec and
// interoperability issues.
// The only allowed fallback pattern is fallbacks from bare specifiers with
// two elements, which are listed in fallback.sub.tentative.html.
const importMap = `
{
"imports": {
"bare": "./resources/log.js?pipe=sub&name=bare",
"./resources/log.js?pipe=sub&name=http-to-builtin": [
"./resources/log.js?pipe=sub&name=http-to-builtin",
"@std/blank"
],
"./resources/log.js?pipe=sub&name=fallback-to-different-url-1": [
"@std/blank",
"./resources/log.js?pipe=sub&name=something-different"
],
"./resources/log.js?pipe=sub&name=fallback-to-different-url-2": [
"@std/none",
"./resources/log.js?pipe=sub&name=something-different2"
],
"./resources/log.js?pipe=sub&name=fallback-to-different-origin-1": [
"@std/blank",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=fallback-to-different-origin-1"
],
"./resources/log.js?pipe=sub&name=fallback-to-different-origin-2": [
"@std/none",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=fallback-to-different-origin-2"
],
"./resources/log.js?pipe=sub&name=more-than-two-values-1": [
"@std/none",
"@std/blank",
"./resources/log.js?pipe=sub&name=more-than-two-values-1"
],
"./resources/log.js?pipe=sub&name=more-than-two-values-2": [
"@std/none",
"./resources/log.js?pipe=sub&name=more-than-two-values-2",
"@std/blank"
],
"./resources/log.js?pipe=sub&name=fallback-from-http": [
"./resources/log.js?pipe=sub&name=non-built-in",
"./resources/log.js?pipe=sub&name=fallback-from-http"
],
"./resources/log.js?pipe=sub&name=fallback-from-data-1": [
"data:text/plain,",
"./resources/log.js?pipe=sub&name=fallback-from-http"
],
"./resources/log.js?pipe=sub&name=fallback-from-data-2": [
"data:text/javascript,log.push('dataURL')",
"./resources/log.js?pipe=sub&name=fallback-from-http"
]
}
}
`;
const tests = {};
for (const key in JSON.parse(importMap).imports) {
if (key === "bare") {
continue;
}
tests[key] =
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR];
}
doTests(importMap, null, tests);
</script>
<body>

View file

@ -0,0 +1,84 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
// This tests is for fallbacks with the pattern of
// `"https://some.external/url": ["@std/x", "https://some.external/url"]`
// which maps "https://some.external/url" to "@std/x" if "@std/x" is
// implemented, or leaves it unmodified otherwise.
//
// This is the primary use case where fallback should work.
// Some other patterns of fallbacks are intentionally blocked due to ongoing
// spec issues. See fallback-disallowed.sub.tentative.html.
const importMap = `
{
"imports": {
"./resources/log.js?pipe=sub&name=blank": [
"@std/blank",
"./resources/log.js?pipe=sub&name=blank"
],
"./resources/log.js?pipe=sub&name=none": [
"@std/none",
"./resources/log.js?pipe=sub&name=none"
],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-blank": [
"@std/blank",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-blank"
],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-none": [
"@std/none",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-none"
],
"./resources/log.js?pipe=sub&name=std-blank": [
"std:blank",
"./resources/log.js?pipe=sub&name=std-blank"
],
"./resources/log.js?pipe=sub&name=std-none": [
"std:none",
"./resources/log.js?pipe=sub&name=std-none"
],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-blank": [
"std:blank",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-blank"
],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-none": [
"std:none",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-none"
]
}
}
`;
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Result.URL indicates that the specifier was not re-mapped by import maps,
// i.e. either considered as a relative path, or fallback occured.
"./resources/log.js?pipe=sub&name=blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"./resources/log.js?pipe=sub&name=none":
[Result.URL, Result.URL, Result.URL, Result.URL],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-none":
[Result.URL, Result.URL, Result.URL, Result.URL],
"./resources/log.js?pipe=sub&name=std-blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"./resources/log.js?pipe=sub&name=std-none":
[Result.URL, Result.URL, Result.URL, Result.URL],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=std-cross-origin-none":
[Result.URL, Result.URL, Result.URL, Result.URL],
};
doTests(importMap, null, tests);
</script>
<body>

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helper.js"></script>
<script>
// "bare/..." (i.e. without leading "./") are bare specifiers
// (not relative paths).
//
// Discussions about notations for builtin modules are ongoing, e.g.
// https://github.com/tc39/proposal-javascript-standard-library/issues/12
// Currently the tests expects two notations are accepted.
// TODO: Once the discussions converge, update the tests.
const importMap = `
{
"imports": {
"bare": "./resources/log.js?pipe=sub&name=bare",
"./resources/log.js?pipe=sub&name=foo": "./resources/log.js?pipe=sub&name=bar",
"./resources/log.js?pipe=sub&name=cross-origin-foo": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-bar",
"./resources/log.js?pipe=sub&name=to-data": "data:text/javascript,log.push('dataURL')",
"./resources/log.js?pipe=sub&name=std-blank": "std:blank",
"./resources/log.js?pipe=sub&name=blank": "@std/blank",
"./resources/log.js?pipe=sub&name=std-none": "std:none",
"./resources/log.js?pipe=sub&name=none": "@std/none",
"./resources/log.js?pipe=sub&name=to-bare": "bare"
}
}
`;
const tests = {
// Arrays of expected results for:
// - <script src type="module">,
// - <script src> (classic script),
// - static import, and
// - dynamic import.
// Currently, Chromium's implementation resolves import maps as a part of
// specifier resolution, and thus failure in import map resolution causes
// a parse error, not fetch error. Therefore, we use Result.PARSE_ERROR
// below. https://crbug.com/928435
// HTTP(S) to HTTP(S).
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=foo":
[Result.URL, Result.URL, "log:bar", "log:bar"],
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=cross-origin-foo":
[Result.URL, Result.URL, "log:cross-origin-bar", "log:cross-origin-bar"],
// HTTP(S) to data:
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=to-data":
[Result.URL, Result.URL, "dataURL", "dataURL"],
// HTTP(S) to built-in.
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=std-blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=blank":
[Result.URL, Result.URL, Result.BUILTIN, Result.BUILTIN],
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=std-none":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=none":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
// HTTP(S) to bare mapping is disabled.
"{{location[server]}}/import-maps/resources/log.js?pipe=sub&name=to-bare":
[Result.URL, Result.URL, Result.PARSE_ERROR, Result.PARSE_ERROR],
};
doTests(importMap, null, tests);
</script>
<body>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="importmap">
{
"imports": {
"./resources/log.js?pipe=sub&name=A": "./resources/log.js?pipe=sub&name=B"
}
}
</script>
<script>
const log = [];
// This test reflects the Chromium's current implementation.
// If the import map resolution is moved into the fetch spec, the module map's
// key will become the URL/specifier BEFORE import map resolution.
// https://crbug.com/928435
promise_test(() => {
return import("./resources/log.js?pipe=sub&name=A")
.then(() => import("./resources/log.js?pipe=sub&name=B"))
.then(() => assert_array_equals(log, ["log:B"]))
},
"Module map's key is the URL after import map resolution");
</script>

View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
setup({allow_uncaught_exception : true});
// Hacky glue code to run Jest-based tests as WPT tests.
// Only supports resolving.js.
function require(name) {
return {
'URL': URL,
'parseFromString': parseFromString,
'resolve': resolve
};
}
function expect(v) {
return {
toMatchURL: expected => assert_equals(v, expected),
toThrow: expected => assert_throws(expected(), v)
};
}
let current_message = '';
function describe(message, f) {
const old = current_message;
if (current_message !== '') {
current_message += ' / ';
}
current_message += message;
f();
current_message = old;
}
function it(message, f) {
const old = current_message;
if (current_message !== '') {
current_message += ' / ';
}
current_message += message;
test(t => t.step_func(f)(), current_message);
current_message = old;
}
// Creates a new Document (via <iframe>) and add an inline import map.
// Currently document.write() is used to make everything synchronous, which
// is just needed for running the existing Jest-based tests easily.
function parseFromString(mapString, mapBaseURL) {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.contentDocument.write(`
<base href="${mapBaseURL}">
<script>
let isError = false;
function onError() {
isError = true;
}
</sc` + `ript>
<script type="importmap" onerror="onError()">
${mapString}
</sc` + `ript>
`);
iframe.contentDocument.close();
return iframe;
}
// URL resolution is tested using Chromium's `internals`.
// TODO(hiroshige): Remove the Chromium-specific dependency.
function resolve(specifier, map, baseURL) {
return internals.resolveModuleSpecifier(specifier,
baseURL,
map.contentDocument);
}
</script>
<!--
resolving.js is
https://github.com/WICG/import-maps/blob/master/reference-implementation/__tests__/resolving.js
-->
<script type="module" src="resources/resolving.js"></script>

View file

@ -0,0 +1 @@
log.push("log:{{GET[name]}}");

View file

@ -0,0 +1 @@
Access-Control-Allow-Origin: *

View file

@ -0,0 +1,206 @@
'use strict';
const { URL } = require('url');
const { parseFromString } = require('../lib/parser.js');
const { resolve } = require('../lib/resolver.js');
const mapBaseURL = new URL('https://example.com/app/index.html');
const scriptURL = new URL('https://example.com/js/app.mjs');
function makeResolveUnderTest(mapString) {
const map = parseFromString(mapString, mapBaseURL);
return specifier => resolve(specifier, map, scriptURL);
}
describe('Unmapped', () => {
const resolveUnderTest = makeResolveUnderTest(`{}`);
it('should resolve ./ specifiers as URLs', () => {
expect(resolveUnderTest('./foo')).toMatchURL('https://example.com/js/foo');
expect(resolveUnderTest('./foo/bar')).toMatchURL('https://example.com/js/foo/bar');
expect(resolveUnderTest('./foo/../bar')).toMatchURL('https://example.com/js/bar');
expect(resolveUnderTest('./foo/../../bar')).toMatchURL('https://example.com/bar');
});
it('should resolve ../ specifiers as URLs', () => {
expect(resolveUnderTest('../foo')).toMatchURL('https://example.com/foo');
expect(resolveUnderTest('../foo/bar')).toMatchURL('https://example.com/foo/bar');
expect(resolveUnderTest('../../../foo/bar')).toMatchURL('https://example.com/foo/bar');
});
it('should resolve / specifiers as URLs', () => {
expect(resolveUnderTest('/foo')).toMatchURL('https://example.com/foo');
expect(resolveUnderTest('/foo/bar')).toMatchURL('https://example.com/foo/bar');
expect(resolveUnderTest('/../../foo/bar')).toMatchURL('https://example.com/foo/bar');
expect(resolveUnderTest('/../foo/../bar')).toMatchURL('https://example.com/bar');
});
it('should parse absolute fetch-scheme URLs', () => {
expect(resolveUnderTest('about:good')).toMatchURL('about:good');
expect(resolveUnderTest('https://example.net')).toMatchURL('https://example.net/');
expect(resolveUnderTest('https://ex%41mple.com/')).toMatchURL('https://example.com/');
expect(resolveUnderTest('https:example.org')).toMatchURL('https://example.org/');
expect(resolveUnderTest('https://///example.com///')).toMatchURL('https://example.com///');
});
it('should fail for absolute non-fetch-scheme URLs', () => {
expect(() => resolveUnderTest('mailto:bad')).toThrow(TypeError);
expect(() => resolveUnderTest('import:bad')).toThrow(TypeError);
expect(() => resolveUnderTest('javascript:bad')).toThrow(TypeError);
expect(() => resolveUnderTest('wss:bad')).toThrow(TypeError);
});
it('should fail for strings not parseable as absolute URLs and not starting with ./ ../ or /', () => {
expect(() => resolveUnderTest('foo')).toThrow(TypeError);
expect(() => resolveUnderTest('\\foo')).toThrow(TypeError);
expect(() => resolveUnderTest(':foo')).toThrow(TypeError);
expect(() => resolveUnderTest('@foo')).toThrow(TypeError);
expect(() => resolveUnderTest('%2E/foo')).toThrow(TypeError);
expect(() => resolveUnderTest('%2E%2E/foo')).toThrow(TypeError);
expect(() => resolveUnderTest('.%2Ffoo')).toThrow(TypeError);
expect(() => resolveUnderTest('https://ex ample.org/')).toThrow(TypeError);
expect(() => resolveUnderTest('https://example.com:demo')).toThrow(TypeError);
expect(() => resolveUnderTest('http://[www.example.com]/')).toThrow(TypeError);
});
});
describe('Mapped using the "imports" key only (no scopes)', () => {
it('should fail when the mapping is to an empty array', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"moment": null,
"lodash": []
}
}`);
expect(() => resolveUnderTest('moment')).toThrow(TypeError);
expect(() => resolveUnderTest('lodash')).toThrow(TypeError);
});
describe('Package-like scenarios', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"moment/": "/node_modules/moment/src/",
"lodash-dot": "./node_modules/lodash-es/lodash.js",
"lodash-dot/": "./node_modules/lodash-es/",
"lodash-dotdot": "../node_modules/lodash-es/lodash.js",
"lodash-dotdot/": "../node_modules/lodash-es/"
}
}`);
it('should work for package main modules', () => {
expect(resolveUnderTest('moment')).toMatchURL('https://example.com/node_modules/moment/src/moment.js');
expect(resolveUnderTest('lodash-dot')).toMatchURL('https://example.com/app/node_modules/lodash-es/lodash.js');
expect(resolveUnderTest('lodash-dotdot')).toMatchURL('https://example.com/node_modules/lodash-es/lodash.js');
});
it('should work for package submodules', () => {
expect(resolveUnderTest('moment/foo')).toMatchURL('https://example.com/node_modules/moment/src/foo');
expect(resolveUnderTest('lodash-dot/foo')).toMatchURL('https://example.com/app/node_modules/lodash-es/foo');
expect(resolveUnderTest('lodash-dotdot/foo')).toMatchURL('https://example.com/node_modules/lodash-es/foo');
});
it('should work for package names that end in a slash by just passing through', () => {
// TODO: is this the right behavior, or should we throw?
expect(resolveUnderTest('moment/')).toMatchURL('https://example.com/node_modules/moment/src/');
});
it('should still fail for package modules that are not declared', () => {
expect(() => resolveUnderTest('underscore/')).toThrow(TypeError);
expect(() => resolveUnderTest('underscore/foo')).toThrow(TypeError);
});
});
describe('Tricky specifiers', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"package/withslash": "/node_modules/package-with-slash/index.mjs",
"not-a-package": "/lib/not-a-package.mjs",
".": "/lib/dot.mjs",
"..": "/lib/dotdot.mjs",
"..\\\\": "/lib/dotdotbackslash.mjs",
"%2E": "/lib/percent2e.mjs",
"%2F": "/lib/percent2f.mjs"
}
}`);
it('should work for explicitly-mapped specifiers that happen to have a slash', () => {
expect(resolveUnderTest('package/withslash')).toMatchURL('https://example.com/node_modules/package-with-slash/index.mjs');
});
it('should work when the specifier has punctuation', () => {
expect(resolveUnderTest('.')).toMatchURL('https://example.com/lib/dot.mjs');
expect(resolveUnderTest('..')).toMatchURL('https://example.com/lib/dotdot.mjs');
expect(resolveUnderTest('..\\')).toMatchURL('https://example.com/lib/dotdotbackslash.mjs');
expect(resolveUnderTest('%2E')).toMatchURL('https://example.com/lib/percent2e.mjs');
expect(resolveUnderTest('%2F')).toMatchURL('https://example.com/lib/percent2f.mjs');
});
it('should fail for attempting to get a submodule of something not declared with a trailing slash', () => {
expect(() => resolveUnderTest('not-a-package/foo')).toThrow(TypeError);
});
});
describe('URL-like specifiers', () => {
const resolveUnderTest = makeResolveUnderTest(`{
"imports": {
"/node_modules/als-polyfill/index.mjs": "@std/kv-storage",
"/lib/foo.mjs": "./more/bar.mjs",
"./dotrelative/foo.mjs": "/lib/dot.mjs",
"../dotdotrelative/foo.mjs": "/lib/dotdot.mjs",
"/lib/no.mjs": null,
"./dotrelative/no.mjs": [],
"/": "/lib/slash-only.mjs",
"./": "/lib/dotslash-only.mjs",
"/test": "/lib/test1.mjs",
"../test": "/lib/test2.mjs"
}
}`);
it('should remap to built-in modules', () => {
expect(resolveUnderTest('/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
expect(resolveUnderTest('https://example.com/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
expect(resolveUnderTest('https://///example.com/node_modules/als-polyfill/index.mjs')).toMatchURL('import:@std/kv-storage');
});
it('should remap to other URLs', () => {
expect(resolveUnderTest('https://example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
expect(resolveUnderTest('https://///example.com/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
expect(resolveUnderTest('/lib/foo.mjs')).toMatchURL('https://example.com/app/more/bar.mjs');
expect(resolveUnderTest('https://example.com/app/dotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dot.mjs');
expect(resolveUnderTest('../app/dotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dot.mjs');
expect(resolveUnderTest('https://example.com/dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
expect(resolveUnderTest('../dotdotrelative/foo.mjs')).toMatchURL('https://example.com/lib/dotdot.mjs');
});
it('should fail for URLs that remap to empty arrays', () => {
expect(() => resolveUnderTest('https://example.com/lib/no.mjs')).toThrow(TypeError);
expect(() => resolveUnderTest('/lib/no.mjs')).toThrow(TypeError);
expect(() => resolveUnderTest('../lib/no.mjs')).toThrow(TypeError);
expect(() => resolveUnderTest('https://example.com/app/dotrelative/no.mjs')).toThrow(TypeError);
expect(() => resolveUnderTest('/app/dotrelative/no.mjs')).toThrow(TypeError);
expect(() => resolveUnderTest('../app/dotrelative/no.mjs')).toThrow(TypeError);
});
it('should remap URLs that are just composed from / and .', () => {
expect(resolveUnderTest('https://example.com/')).toMatchURL('https://example.com/lib/slash-only.mjs');
expect(resolveUnderTest('/')).toMatchURL('https://example.com/lib/slash-only.mjs');
expect(resolveUnderTest('../')).toMatchURL('https://example.com/lib/slash-only.mjs');
expect(resolveUnderTest('https://example.com/app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs');
expect(resolveUnderTest('/app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs');
expect(resolveUnderTest('../app/')).toMatchURL('https://example.com/lib/dotslash-only.mjs');
});
it('should use the last entry\'s address when URL-like specifiers parse to the same absolute URL', () => {
expect(resolveUnderTest('/test')).toMatchURL('https://example.com/lib/test2.mjs');
});
});
});

View file

@ -0,0 +1,205 @@
let log = [];
function expect_log(test, expected_log) {
test.step_func_done(() => {
const actual_log = log;
log = [];
assert_array_equals(actual_log, expected_log, 'fallback log');
})();
}
// Results of resolving a specifier using import maps.
const Result = {
// A built-in module (std:blank) is loaded.
BUILTIN: "builtin",
// A failure considered as a fetch error in a module script tree.
// <script>'s error event is fired.
FETCH_ERROR: "fetch_error",
// A failure considered as a parse error in a module script tree.
// Window's error event is fired.
PARSE_ERROR: "parse_error",
// The specifier is considered as a relative or absolute URL.
// Specifier Expected log
// ------------------------- ----------------------
// ...?name=foo log:foo
// data:...log('foo') foo
// Others, e.g. @std/blank relative:@std/blank
// ------------------------- ----------------------
// (The last case assumes a file `@std/blank` that logs `relative:@std/blank`
// exists)
URL: "URL",
};
const Handler = {
// Handlers for <script> element cases.
// Note that on a parse error both WindowErrorEvent and ScriptLoadEvent are
// called.
ScriptLoadEvent: "<script> element's load event handler",
ScriptErrorEvent: "<script> element's error event handler",
WindowErrorEvent: "window's error event handler",
// Handlers for dynamic imports.
DynamicImportResolve: "dynamic import resolve",
DynamicImportReject: "dynamic import reject",
};
// Returns a map with Handler.* as the keys.
function getHandlers(t, specifier, expected) {
let handlers = {};
handlers[Handler.ScriptLoadEvent] = t.unreached_func("Shouldn't load");
handlers[Handler.ScriptErrorEvent] =
t.unreached_func("script's error event shouldn't be fired");
handlers[Handler.WindowErrorEvent] =
t.unreached_func("window's error event shouldn't be fired");
handlers[Handler.DynamicImportResolve] =
t.unreached_func("dynamic import promise shouldn't be resolved");
handlers[Handler.DynamicImportReject] =
t.unreached_func("dynamic import promise shouldn't be rejected");
if (expected === Result.FETCH_ERROR) {
handlers[Handler.ScriptErrorEvent] = () => expect_log(t, []);
handlers[Handler.DynamicImportReject] = () => expect_log(t, []);
} else if (expected === Result.PARSE_ERROR) {
let error_occurred = false;
handlers[Handler.WindowErrorEvent] = () => { error_occurred = true; };
handlers[Handler.ScriptLoadEvent] = t.step_func(() => {
// Even if a parse error occurs, load event is fired (after
// window.onerror is called), so trigger the load handler only if
// there was no previous window.onerror call.
assert_true(error_occurred, "window.onerror should be fired");
expect_log(t, []);
});
handlers[Handler.DynamicImportReject] = t.step_func(() => {
assert_false(error_occurred,
"window.onerror shouldn't be fired for dynamic imports");
expect_log(t, []);
});
} else {
let expected_log;
if (expected === Result.BUILTIN) {
expected_log = [];
} else if (expected === Result.URL) {
const match_data_url = specifier.match(/data:.*log\.push\('(.*)'\)/);
const match_log_js = specifier.match(/name=(.*)/);
if (match_data_url) {
expected_log = [match_data_url[1]];
} else if (match_log_js) {
expected_log = ["log:" + match_log_js[1]];
} else {
expected_log = ["relative:" + specifier];
}
} else {
expected_log = [expected];
}
handlers[Handler.ScriptLoadEvent] = () => expect_log(t, expected_log);
handlers[Handler.DynamicImportResolve] = () => expect_log(t, expected_log);
}
return handlers;
}
// Creates an <iframe> and run a test inside the <iframe>
// to separate the module maps and import maps in each test.
function testInIframe(importMapString, importMapBaseURL, testScript) {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
if (!importMapBaseURL) {
importMapBaseURL = document.baseURI;
}
let content = `
<script src="/resources/testharness.js"></script>
<script src="/import-maps/resources/test-helper.js"></script>
<base href="${importMapBaseURL}">
`;
if (importMapString) {
content += `
<script type="importmap">
${importMapString}
</sc` + `ript>
`;
}
content += `
<body>
<script>
setup({ allow_uncaught_exception: true });
${testScript}
</sc` + `ript>
`;
iframe.contentDocument.write(content);
iframe.contentDocument.close();
fetch_tests_from_window(iframe.contentWindow);
}
function testScriptElement(importMapString, importMapBaseURL, specifier, expected, type) {
testInIframe(importMapString, importMapBaseURL, `
const t = async_test("${specifier}: <script src type=${type}>");
const handlers = getHandlers(t, "${specifier}", "${expected}");
const script = document.createElement("script");
script.setAttribute("type", "${type}");
script.setAttribute("src", "${specifier}");
script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
document.body.appendChild(script);
`);
}
function testStaticImport(importMapString, importMapBaseURL, specifier, expected) {
testInIframe(importMapString, importMapBaseURL, `
const t = async_test("${specifier}: static import");
const handlers = getHandlers(t, "${specifier}", "${expected}");
const script = document.createElement("script");
script.setAttribute("type", "module");
script.setAttribute("src",
"/import-maps/static-import.js?pipe=sub(none)&url=" +
encodeURIComponent("${specifier}"));
script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
document.body.appendChild(script);
`);
}
function testDynamicImport(importMapString, importMapBaseURL, specifier, expected, type) {
testInIframe(importMapString, importMapBaseURL, `
const t = async_test("${specifier}: dynamic import (from ${type})");
const handlers = getHandlers(t, "${specifier}", "${expected}");
const script = document.createElement("script");
script.setAttribute("type", "${type}");
script.innerText =
"import(\\"${specifier}\\")" +
".then(handlers[Handler.DynamicImportResolve], " +
"handlers[Handler.DynamicImportReject]);";
script.addEventListener("error",
t.unreached_func("top-level inline script shouldn't error"));
document.body.appendChild(script);
`);
}
function doTests(importMapString, importMapBaseURL, tests) {
window.addEventListener("load", () => {
for (const specifier in tests) {
// <script src> (module scripts)
testScriptElement(importMapString, importMapBaseURL, specifier,
tests[specifier][0], "module");
// <script src> (classic scripts)
testScriptElement(importMapString, importMapBaseURL, specifier,
tests[specifier][1], "text/javascript");
// static imports.
testStaticImport(importMapString, importMapBaseURL, specifier,
tests[specifier][2]);
// dynamic imports from a module script.
testDynamicImport(importMapString, importMapBaseURL, specifier,
tests[specifier][3], "module");
// dynamic imports from a classic script.
testDynamicImport(importMapString, importMapBaseURL, specifier,
tests[specifier][3], "text/javascript");
}
});
}

View file

@ -0,0 +1 @@
import "{{GET[url]}}";

View file

@ -78,12 +78,12 @@ callback XRFrameRequestCallback = void (DOMHighResTimeStamp time, XRFrame frame)
[SecureContext, Exposed=Window] interface XRFrame {
readonly attribute XRSession session;
XRViewerPose? getViewerPose(optional XRReferenceSpace referenceSpace);
XRInputPose? getInputPose(XRInputSource inputSource, optional XRReferenceSpace referenceSpace);
XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
XRPose? getPose(XRSpace space, XRSpace relativeTo);
};
[SecureContext, Exposed=Window] interface XRSpace : EventTarget {
XRRigidTransform? getTransformTo(XRSpace other);
};
enum XRReferenceSpaceType {
@ -159,8 +159,12 @@ interface XRRay {
readonly attribute Float32Array matrix;
};
[SecureContext, Exposed=Window] interface XRViewerPose {
[SecureContext, Exposed=Window] interface XRPose {
readonly attribute XRRigidTransform transform;
readonly attribute boolean emulatedPosition;
};
[SecureContext, Exposed=Window] interface XRViewerPose : XRPose {
readonly attribute FrozenArray<XRView> views;
};
@ -180,13 +184,8 @@ enum XRTargetRayMode {
interface XRInputSource {
readonly attribute XRHandedness handedness;
readonly attribute XRTargetRayMode targetRayMode;
};
[SecureContext, Exposed=Window]
interface XRInputPose {
readonly attribute boolean emulatedPosition;
readonly attribute XRRay targetRay;
readonly attribute XRRigidTransform? gripTransform;
readonly attribute XRSpace targetRaySpace;
readonly attribute XRSpace? gripSpace;
};
[SecureContext, Exposed=Window] interface XRLayer {};

View file

@ -72,7 +72,15 @@
// notifications.
function waitForNotification(t, f) {
requestAnimationFrame(function() {
requestAnimationFrame(function() { t.step_timeout(f); });
requestAnimationFrame(function() { t.step_timeout(f, 0); });
});
}
// If you need to wait until the IntersectionObserver algorithm has a chance
// to run, but don't need to wait for delivery of the notifications...
function waitForFrame(t, f) {
requestAnimationFrame(function() {
t.step_timeout(f, 0);
});
}
@ -85,9 +93,19 @@ function waitForNotification(t, f) {
//
// Following these rules will ensure that the test suite will not abort before
// all test steps have run.
function runTestCycle(f, description) {
//
// If the 'delay' parameter to the IntersectionObserver constructor is used,
// tests will need to add the same delay to their runTestCycle invocations, to
// wait for notifications to be generated and delivered.
function runTestCycle(f, description, delay) {
async_test(function(t) {
waitForNotification(t, t.step_func_done(f));
if (delay) {
step_timeout(() => {
waitForNotification(t, t.step_func_done(f));
}, delay);
} else {
waitForNotification(t, t.step_func_done(f));
}
}, description);
}
@ -174,4 +192,4 @@ function checkJsonEntries(actual, expected, description) {
function checkIsIntersecting(entries, i, expected) {
assert_equals(entries[i].isIntersecting, expected,
'entries[' + i + '].target.isIntersecting equals ' + expected);
}
}

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<div id="target">target</div>
<script>
var delay = 100;
var results = [];
function waitForNotification(f) {
setTimeout(() => {
requestAnimationFrame(function () {
requestAnimationFrame(function () {
setTimeout(f)
})
})
}, delay)
}
window.addEventListener("message", event => {
waitForNotification(() => {
window.parent.postMessage(results.map(e => e.isVisible), "*");
results = [];
});
});
onload = () => {
var target = document.getElementById("target");
var observer = new IntersectionObserver(entries => {
results = entries;
}, {trackVisibility: true, delay: delay});
observer.observe(document.getElementById("target"));
window.parent.postMessage("", "*");
};
</script>

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(45deg);
}
}
#occluder {
will-change: transform;
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<div id="target"></div>
<div id="occluder"></div>
<script>
var vw = document.documentElement.clientWidth;
var vh = document.documentElement.clientHeight;
var delay = 100;
var entries = [];
var target;
var occluder;
runTestCycle(function() {
target = document.getElementById("target");
occluder = document.getElementById("occluder");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 in a single document using the implicit root, with an animated occluding element.", delay);
function step0() {
occluder.style.animation = "rotate .1s linear";
step_timeout(() => {
runTestCycle(step1, "occluder.style.animation = 'rotate .1s linear'", delay);
}, 50);
checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, vw, 0, vh, true, true]);
}
function step1() {
checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, vw, 0, vh, true, false]);
}
</script>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
#occluder {
margin-top: 10px;
width: 100px;
height: 100px;
background-color: blue;
filter: blur(50px);
}
</style>
<div id="target"></div>
<div id="occluder"></div>
<script>
var delay = 100;
var entries = [];
var target;
var occluder;
runTestCycle(function() {
target = document.getElementById("target");
occluder = document.getElementById("occluder");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 in a single document using the implicit root, with an occluding element.", delay);
function step0() {
// Occluding elements with opacity=0 should not affect target visibility.
occluder.style.opacity = "0";
runTestCycle(step2, "occluder.style.opacity = 0", delay);
// First notification should report occlusion due to blur filter.
checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
}
function step2() {
checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
</script>

View file

@ -0,0 +1,66 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
iframe {
width: 100px;
height: 100px;
border: 0;
}
#box-shadow {
display: inline-block;
box-shadow: -50px -50px 0 50px rgba(255, 0, 0, 0.7);
}
</style>
<iframe id=target srcdoc="<!DOCTYPE html><div>Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum.</div>"></iframe><div id=box-shadow></div>
<script>
var delay = 100;
var entries = [];
var target;
var occluder;
runTestCycle(function() {
target = document.getElementById("target");
occluder = document.getElementById("box-shadow");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
let observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 observing an iframe element.", delay);
function step0() {
occluder.style.boxShadow = "none";
runTestCycle(step1, 'occluder.style.boxShadow = "none"', delay);
assert_equals(entries.length, 1, "Initial notification.");
assert_equals(entries[0].isVisible, false, "Initially occluded.");
}
function step1() {
occluder.style.boxShadow = "";
runTestCycle(step2, 'occluder.style.boxShadow = ""', delay);
assert_equals(entries.length, 2, "Notification after removing box shadow.");
assert_equals(entries[1].isVisible, true, "Visible when box shadow removed.");
}
function step2() {
assert_equals(entries.length, 3, "Notification after re-adding box shadow.");
assert_equals(entries[2].isVisible, false, "Occluded when box shadow re-added.");
}
</script>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
</style>
<div id="container">
<iframe src="http://{{domains[www1]}}:{{ports[http][0]}}/intersection-observer/resources/v2-subframe.html"></iframe>
</div>
<script>
async_test(function(t) {
let container = document.getElementById("container");
let iframe = document.querySelector("iframe");
function step0(event) {
assert_equals(event.data,"");
}
function step1(event) {
container.style.opacity = "0.99";
assert_equals(JSON.stringify(event.data),
JSON.stringify([true]));
}
function step2(event) {
container.style.opacity = "";
assert_equals(JSON.stringify(event.data),
JSON.stringify([false]));
}
function step3(event) {
container.style.transform = "skew(30deg)";
assert_equals(JSON.stringify(event.data),
JSON.stringify([true]));
}
function step4(event) {
assert_equals(JSON.stringify(event.data),
JSON.stringify([false]));
}
let steps = [step0, step1, step2, step3, step4];
window.addEventListener("message", event => {
if (steps.length) {
t.step_func(steps.shift(), event);
waitForFrame(t, () => {
iframe.contentWindow.postMessage("", "*")
});
} else {
t.done();
}
});
}, "Intersection observer V2 test with visual effects on iframe.");
</script>

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
iframe {
width: 300px;
height: 150px;
border: none;
}
#occluder {
will-change: transform;
width: 100px;
height: 100px;
background-color: green;
}
</style>
<iframe src="http://{{domains[www1]}}:{{ports[http][0]}}/intersection-observer/resources/v2-subframe.html"></iframe>
<div id="occluder"></div>
<script>
async_test(function(t) {
let iframe = document.querySelector("iframe");
let occluder = document.getElementById("occluder");
function step0(event) {
assert_equals(event.data,"");
}
function step1(event) {
occluder.style.marginTop = "-150px";
assert_equals(JSON.stringify(event.data),
JSON.stringify([true]));
}
function step2(event) {
occluder.style.marginTop = "";
assert_equals(JSON.stringify(event.data),
JSON.stringify([false]));
}
function step3(event) {
assert_equals(JSON.stringify(event.data),
JSON.stringify([true]));
}
let steps = [step0, step1, step2, step3];
window.addEventListener("message", event => {
if (steps.length) {
t.step_func(steps.shift(), event);
waitForFrame(t, () => {
iframe.contentWindow.postMessage("", "*");
});
} else {
t.done();
}
});
}, "Intersection observer V2 test with occlusion of target in iframe.");
</script>

View file

@ -0,0 +1,75 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
#occluder {
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<div id="target"></div>
<div id="occluder"></div>
<script>
async_test(t => {
let entries = [];
let delay = 100;
let target = document.getElementById("target");
let occluder = document.getElementById("occluder");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
let observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
// The first notification should be sent without delay.
waitForNotification(t, t.step_func(step0));
function waitForDelay(timerExpiredBeforeLastFrame) {
requestAnimationFrame(t.step_func(() => {
if (timerExpiredBeforeLastFrame) {
// New notifications should have been generated during the previous
// frame and delivered by now.
assert_equals(entries.length, 2);
assert_greater_than(entries[1].time - entries[0].time, delay);
assert_false(entries[1].isVisible);
t.done();
} else {
// Observer may not have updated yet. Wait for next frame.
let timerExpired = performance.now() - entries[0].time >= delay;
waitForDelay(timerExpired);
}
}));
}
function step0() {
assert_equals(entries.length, 1);
assert_true(entries[0].isVisible);
// This should trigger a notification on the next run.
occluder.style.marginTop = "-10px";
// Enter a rAF loop until the delay timer expires.
waitForDelay(false);
}
}, "'delay' parameter throttles frequency of notifications.");
</script>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
iframe {
width: 150px;
height: 100px;
border: 0;
}
</style>
<iframe srcdoc="<!DOCTYPE html><div>Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum. Lorem ipsum.</div>"></iframe>
<script>
var delay = 100;
var entries = [];
var target;
runTestCycle(function() {
target = document.querySelector("iframe");
assert_true(!!target, "target exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 observing an iframe element.", delay);
function step0() {
checkLastEntry(entries, 0, [0, 150, 0, 100, 0, 150, 0, 100, 0, 800, 0, 600, true, true]);
}
</script>

View file

@ -0,0 +1,71 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
#effects {
opacity: 1;
filter: none;
}
</style>
<div id="effects">
<div id="target"></div>
</div>
<script>
var delay = 100;
var entries = [];
var target;
var effects;
runTestCycle(function() {
target = document.getElementById("target");
effects = document.getElementById("effects");
assert_true(!!target, "target exists");
assert_true(!!effects, "effects exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 in a single document using the implicit root, with a non-zero opacity ancestor.", delay);
function step0() {
effects.style.opacity = "0.99";
runTestCycle(step1, "effects.style.opacity = 0.99", delay);
checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
function step1() {
effects.style.opacity = "1";
runTestCycle(step2, "effects.style.opacity = 1", delay);
checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
}
function step2() {
effects.style.filter = "grayscale(50%)";
runTestCycle(step3, "effects.style.filter = grayscale(50%)", delay);
checkLastEntry(entries, 2, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
function step3() {
checkLastEntry(entries, 3, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
}
</script>

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
#occluder {
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<div id="target"></div>
<svg id="svg" style="display: block">
<foreignObject>
<div id="occluder"></div>
</foreignObject>
</svg>
<script>
var delay = 100;
var entries = [];
var target;
var occluder;
runTestCycle(function() {
target = document.getElementById("target");
occluder = document.getElementById("occluder");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 in a single document using the implicit root, with an occluding element.", delay);
function step0() {
svg.style.marginTop = "-10px";
runTestCycle(step1, "svg.style.marginTop = '-10px'", delay);
checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
function step1() {
// Occluding elements with opacity=0 should not affect target visibility.
svg.style.opacity = "0";
runTestCycle(step2, "occluder.style.opacity = 0", delay);
checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
}
function step2() {
checkLastEntry(entries, 2, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
</script>

View file

@ -0,0 +1,66 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
#occluder {
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<div id="target"></div>
<div id="occluder"></div>
<script>
var delay = 100;
var entries = [];
var target;
var occluder;
runTestCycle(function() {
target = document.getElementById("target");
occluder = document.getElementById("occluder");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: delay});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.", delay);
}, "IntersectionObserverV2 in a single document using the implicit root, with an occluding element.", delay);
function step0() {
occluder.style.marginTop = "-10px";
runTestCycle(step1, "occluder.style.marginTop = '-10px'", delay);
checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
function step1() {
// Occluding elements with opacity=0 should not affect target visibility.
occluder.style.opacity = "0";
runTestCycle(step2, "occluder.style.opacity = 0", delay);
checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
}
function step2() {
checkLastEntry(entries, 2, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
</script>

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