1 // NOTE: this file needs to be split up rather than expanded. See ../location.sub.html for some
2 // extracted tests. Tracked by https://github.com/web-platform-tests/wpt/issues/4934.
6 * https://html.spec.whatwg.org/multipage/#the-link-element
7 * https://html.spec.whatwg.org/multipage/#styling
8 * https://html.spec.whatwg.org/multipage/#prepare-a-script
9 * https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm
10 * https://html.spec.whatwg.org/multipage/#track-url
11 * https://html.spec.whatwg.org/multipage/#concept-form-submit
12 * https://html.spec.whatwg.org/multipage/#set-the-frozen-base-url
13 * https://dom.spec.whatwg.org/#dom-node-baseuri
14 * https://html.spec.whatwg.org/multipage/#the-a-element
15 * https://html.spec.whatwg.org/multipage/#dom-worker
16 * https://html.spec.whatwg.org/multipage/#dom-sharedworker
17 * https://html.spec.whatwg.org/multipage/#dom-eventsource
18 * https://html.spec.whatwg.org/multipage/#dom-xmldocument-load
19 * https://html.spec.whatwg.org/multipage/#dom-open
20 * http://url.spec.whatwg.org/#dom-url-search
21 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=24148
22 * https://xhr.spec.whatwg.org/#the-open()-method
23 * https://html.spec.whatwg.org/multipage/#set-up-a-worker-script-settings-object
24 * https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-importscripts
25 * https://html.spec.whatwg.org/multipage/#parse-a-websocket-url's-components
26 * https://html.spec.whatwg.org/multipage/#dom-websocket-url
27 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=23968
28 * http://dev.w3.org/csswg/cssom/#requirements-on-user-agents-implementing-the-xml-stylesheet-processing-instruction
29 * http://url.spec.whatwg.org/#dom-url
31 setup({explicit_done:true});
33 var encoding = '{{GET[encoding]}}';
34 var input_url = 'resources/resource.py?q=\u00E5&encoding=' + encoding + '&type=';
35 ('html css js worker sharedworker worker_importScripts sharedworker_importScripts worker_worker worker_sharedworker sharedworker_worker '+
36 'sharedworker_sharedworker eventstream png svg xmlstylesheet_css video webvtt').split(' ').forEach(function(str) {
37 window['input_url_'+str] = input_url + str;
39 var blank = 'resources/blank.py?encoding=' + encoding;
40 var stash_put = 'resources/stash.py?q=\u00E5&action=put&id=';
41 var stash_take = 'resources/stash.py?action=take&id=';
47 'windows-1251':'%26%23229%3B'
49 var expected_current = expected_obj[encoding];
50 var expected_utf8 = expected_obj['utf-8'];
52 function msg(expected, got) {
53 return 'expected substring '+expected+' got '+got;
56 function poll_for_stash(test_obj, uuid, expected) {
57 var start = new Date();
58 var poll = test_obj.step_func(function () {
59 var xhr = new XMLHttpRequest();
60 xhr.open('GET', stash_take + uuid);
61 xhr.onload = test_obj.step_func(function(e) {
62 if (xhr.response == "") {
63 if (new Date() - start > 10000) {
64 // If we set the status to TIMEOUT here we avoid a race between the
65 // page and the test timing out
66 test_obj.force_timeout();
68 test_obj.step_timeout(poll, 200);
70 assert_equals(xhr.response, expected);
76 test_obj.step_timeout(poll, 200);
79 // loading html (or actually svg to support <embed>)
80 function test_load_nested_browsing_context(tag, attr, spec_url) {
81 subsetTestByKey('nested-browsing', async_test, function() {
82 var id = 'test_load_nested_browsing_context_'+tag;
83 var elm = document.createElement(tag);
84 elm.setAttribute(attr, input_url_svg);
86 document.body.appendChild(elm);
87 this.add_cleanup(function() {
88 document.body.removeChild(elm);
90 elm.onload = this.step_func_done(function() {
91 assert_equals(window[id].document.documentElement.textContent, expected_current);
94 }, 'load nested browsing context <'+tag+' '+attr+'>');
97 spec_url_load_nested_browsing_context = {
98 frame:'https://html.spec.whatwg.org/multipage/#process-the-frame-attributes',
99 iframe:'https://html.spec.whatwg.org/multipage/#process-the-iframe-attributes',
100 object:'https://html.spec.whatwg.org/multipage/#the-object-element',
101 embed:'https://html.spec.whatwg.org/multipage/#the-embed-element-setup-steps'
104 'frame src, iframe src, object data, embed src'.split(', ').forEach(function(str) {
105 var arr = str.split(' ');
106 test_load_nested_browsing_context(arr[0], arr[1], spec_url_load_nested_browsing_context[arr[0]]);
109 // loading css with <link>
110 subsetTestByKey('loading', async_test, function() {
111 var elm = document.createElement('link');
112 elm.href = input_url_css;
113 elm.rel = 'stylesheet';
114 document.head.appendChild(elm);
115 this.add_cleanup(function() {
116 document.head.removeChild(elm);
118 elm.onload = this.step_func_done(function() {
119 var got = elm.sheet.href;
120 assert_true(elm.sheet.href.indexOf(expected_current) > -1, 'sheet.href ' + msg(expected_current, got));
121 assert_equals(elm.sheet.cssRules[0].style.content, '"'+expected_current+'"', 'sheet.cssRules[0].style.content');
123 }, 'loading css <link>');
126 subsetTestByKey('loading-css', async_test, function() {
127 var elm = document.createElement('script');
128 elm.src = input_url_js + '&var=test_load_js_got';
129 document.head.appendChild(elm); // no cleanup
130 elm.onload = this.step_func_done(function() {
131 assert_equals(window.test_load_js_got, expected_current);
133 }, 'loading js <script>');
136 function test_load_image(tag, attr, spec_url) {
137 subsetTestByKey('loading', async_test, function() {
138 var elm = document.createElement(tag);
139 if (tag == 'input') {
142 elm.setAttribute(attr, input_url_png);
143 document.body.appendChild(elm);
144 this.add_cleanup(function() {
145 document.body.removeChild(elm);
147 elm.onload = this.step_func_done(function() {
148 var got = elm.offsetWidth;
149 assert_equals(got, query_to_image_width[expected_current], msg(expected_current, image_width_to_query[got]));
151 // <video poster> doesn't notify when the image is loaded so we need to poll :-(
153 var check_video_width = function() {
154 var width = elm.offsetWidth;
155 if (width != 300 && width != 0) {
156 clearInterval(interval);
160 if (tag == 'video') {
161 interval = setInterval(check_video_width, 10);
163 }, 'loading image <'+tag+' '+attr+'>');
166 var query_to_image_width = {
171 'default intrinsic width':300
174 var image_width_to_query = {};
176 for (var x in query_to_image_width) {
177 image_width_to_query[query_to_image_width[x]] = x;
181 var spec_url_load_image = {
182 img:'https://html.spec.whatwg.org/multipage/#update-the-image-data',
183 embed:'https://html.spec.whatwg.org/multipage/#the-embed-element-setup-steps',
184 object:'https://html.spec.whatwg.org/multipage/#the-object-element',
185 input:'https://html.spec.whatwg.org/multipage/#image-button-state-(type=image)',
186 video:'https://html.spec.whatwg.org/multipage/#poster-frame'
189 'img src, embed src, object data, input src, video poster'.split(', ').forEach(function(str) {
190 var arr = str.split(' ');
191 test_load_image(arr[0], arr[1], spec_url_load_image[arr[0]]);
194 // XXX test <img srcset> or its successor
197 function test_load_video(tag, use_source_element) {
198 subsetTestByKey('loading', async_test, function() {
199 var elm = document.createElement(tag);
201 if (elm.canPlayType('video/ogg; codecs="theora,flac"')) {
203 } else if (elm.canPlayType('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) {
206 assert_not_equals(video_ext, '', 'no supported video format');
208 if (use_source_element) {
209 source = document.createElement('source');
210 elm.appendChild(source);
214 source.src = input_url_video + '&ext=' + video_ext;
215 elm.preload = 'auto';
217 this.add_cleanup(function() {
218 elm.removeAttribute('src');
219 if (elm.firstChild) {
220 elm.removeChild(elm.firstChild);
224 elm.onloadedmetadata = this.step_func_done(function() {
225 var got = Math.round(elm.duration);
226 assert_equals(got, query_to_video_duration[expected_current], msg(expected_current, video_duration_to_query[got]));
228 }, 'loading video <'+tag+'>' + (use_source_element ? '<source>' : ''));
231 var query_to_video_duration = {
240 var video_duration_to_query = {};
242 for (var x in query_to_video_duration) {
243 video_duration_to_query[query_to_video_duration[x]] = x;
247 'video, audio'.split(', ').forEach(function(str) {
248 test_load_video(str);
249 test_load_video(str, true);
253 subsetTestByKey('loading', async_test, function() {
254 var video = document.createElement('video');
255 var track = document.createElement('track');
256 video.appendChild(track);
257 track.src = input_url_webvtt;
258 track.track.mode = 'showing';
259 track.onload = this.step_func_done(function() {
260 var got = track.track.cues[0].text;
261 assert_equals(got, expected_current);
263 }, 'loading webvtt <track>');
265 // XXX downloading seems hard to automate
267 // <area href download>
270 function test_submit_form(tag, attr) {
271 subsetTestByKey('submit', async_test, function(){
272 var elm = document.createElement(tag);
273 elm.setAttribute(attr, input_url_html);
278 button = document.createElement('button');
280 form = document.createElement('form');
283 form.method = 'post';
284 form.appendChild(button);
285 var iframe = document.createElement('iframe');
286 var id = 'test_submit_form_' + tag;
289 button.type = 'submit';
290 document.body.appendChild(form);
291 document.body.appendChild(iframe);
292 this.add_cleanup(function() {
293 document.body.removeChild(form);
294 document.body.removeChild(iframe);
297 iframe.onload = this.step_func_done(function() {
298 var got = iframe.contentDocument.body.textContent;
302 assert_equals(got, expected_current);
304 }, 'submit form <'+tag+' '+attr+'>');
307 'form action, input formaction, button formaction'.split(', ').forEach(function(str) {
308 var arr = str.split(' ');
309 test_submit_form(arr[0], arr[1]);
313 subsetTestByKey('base-href', async_test, function() {
314 var iframe = document.createElement('iframe');
316 document.body.appendChild(iframe);
317 this.add_cleanup(function() {
318 document.body.removeChild(iframe);
320 iframe.onload = this.step_func_done(function() {
321 var doc = iframe.contentDocument;
322 doc.write('<!doctype html><base href="'+input_url+'"><a href></a>');
324 var got_baseURI = doc.baseURI;
325 assert_true(got_baseURI.indexOf(expected_current) > -1, msg(expected_current, got_baseURI), 'doc.baseURI');
326 var got_a_href = doc.links[0].href;
327 assert_true(got_a_href.indexOf(expected_current) > -1, msg(expected_current, got_a_href), 'a.href');
331 // XXX drag and drop (<a href> or <img src>) seems hard to automate
334 subsetTestByKey('workers', async_test, function() {
335 var worker = new Worker(input_url_worker);
336 worker.onmessage = this.step_func_done(function(e) {
337 assert_equals(e.data, expected_current);
339 }, 'Worker constructor');
342 subsetTestByKey('workers', async_test, function() {
343 var worker = new SharedWorker(input_url_sharedworker);
344 worker.port.onmessage = this.step_func_done(function(e) {
345 assert_equals(e.data, expected_current);
347 }, 'SharedWorker constructor');
350 subsetTestByKey('eventsource', async_test, function() {
351 var source = new EventSource(input_url_eventstream);
352 this.add_cleanup(function() {
355 source.onmessage = this.step_func_done(function(e) {
356 assert_equals(e.data, expected_current);
358 }, 'EventSource constructor');
361 subsetTestByKey('eventsource', test, function() {
362 var source = new EventSource(input_url_eventstream);
364 var got = source.url;
365 assert_true(source.url.indexOf(expected_current) > -1, msg(expected_current, got));
366 }, 'EventSource#url');
369 subsetTestByKey('window-open', async_test, function() {
370 var id = 'test_window_open';
371 var iframe = document.createElement('iframe');
373 document.body.appendChild(iframe);
374 this.add_cleanup(function() {
375 document.body.removeChild(iframe);
377 window.open(input_url_html, id);
378 iframe.onload = this.step_func(function() {
379 var got = iframe.contentDocument.body.textContent;
381 assert_equals(got, expected_current);
387 // a.search, area.search
388 function test_hyperlink_search(tag) {
389 subsetTestByKey('hyperlink-search', test, function() {
390 var elm = document.createElement(tag);
391 var input_arr = input_url_html.split('?');
392 elm.href = input_arr[0];
393 elm.search = '?' + input_arr[1];
394 var got_href = elm.getAttribute('href');
395 assert_true(got_href.indexOf(expected_utf8) > -1, 'href content attribute ' + msg(expected_utf8, got_href));
396 var got_search = elm.search;
397 assert_true(got_search.indexOf(expected_utf8) > -1, 'getting .search '+msg(expected_utf8, got_search));
398 }, '<'+tag+'>.search');
400 'a, area'.split(', ').forEach(function(str) {
401 test_hyperlink_search(str);
405 // history.replaceState
406 function test_history(prop) {
407 subsetTestByKey('history', async_test, function() {
408 var iframe = document.createElement('iframe');
410 document.body.appendChild(iframe);
411 this.add_cleanup(function() {
412 document.body.removeChild(iframe);
414 iframe.onload = this.step_func_done(function() {
415 iframe.contentWindow.history[prop](null, null, input_url_html); // this should resolve against the test's URL, not the iframe's URL
416 var got = iframe.contentWindow.location.href;
417 assert_true(got.indexOf(expected_current) > -1, msg(expected_current, got));
418 assert_equals(got.indexOf('/resources/resources/'), -1, 'url was resolved against the iframe\'s URL instead of the settings object\'s API base URL');
423 'pushState, replaceState'.split(', ').forEach(function(str) {
428 var ns = {svg:'http://www.w3.org/2000/svg', xlink:'http://www.w3.org/1999/xlink'};
430 subsetTestByKey('svg', async_test, function() {
431 SVGAElement; // check support
432 var iframe = document.createElement('iframe');
433 var id = 'test_svg_a';
435 var svg = document.createElementNS(ns.svg, 'svg');
436 var a = document.createElementNS(ns.svg, 'a');
437 a.setAttributeNS(ns.xlink, 'xlink:href', input_url_html);
438 a.setAttribute('target', id);
439 var span = document.createElement('span');
442 document.body.appendChild(iframe);
443 document.body.appendChild(svg);
444 this.add_cleanup(function() {
445 document.body.removeChild(iframe);
446 document.body.removeChild(svg);
449 iframe.onload = this.step_func_done(function() {
450 var got = iframe.contentDocument.body.textContent;
452 assert_equals(got, expected_current);
457 // feImage, image, use
458 function test_svg(func, tag) {
459 subsetTestByKey('svg', async_test, function() {
461 var id = 'test_svg_'+tag;
462 var svg = document.createElementNS(ns.svg, 'svg');
463 var parent = func(svg, id);
464 var elm = document.createElementNS(ns.svg, tag);
465 elm.setAttributeNS(ns.xlink, 'xlink:href', stash_put + uuid + '#foo');
466 parent.appendChild(elm);
467 document.body.appendChild(svg);
468 this.add_cleanup(function() {
469 document.body.removeChild(svg);
471 poll_for_stash(this, uuid, expected_current);
472 }, 'SVG <' + tag + '>');
475 [[function(svg, id) {
476 SVGFEImageElement; // check support
477 var filter = document.createElementNS(ns.svg, 'filter');
478 filter.setAttribute('id', id);
479 svg.appendChild(filter);
480 var rect = document.createElementNS(ns.svg, 'rect');
481 rect.setAttribute('filter', 'url(#'+id+')');
482 svg.appendChild(rect);
485 [function(svg, id) { SVGImageElement; return svg; }, 'image'],
486 [function(svg, id) { SVGUseElement; return svg; }, 'use']].forEach(function(arr) {
487 test_svg(arr[0], arr[1]);
492 subsetTestByKey('xhr', async_test, function() {
493 var xhr = new XMLHttpRequest();
494 xhr.open('GET', input_url_html);
495 xhr.onload = this.step_func_done(function() {
496 assert_equals(xhr.response, expected_utf8);
499 }, 'XMLHttpRequest#open()');
502 subsetTestByKey('workers', async_test, function() {
503 var worker = new Worker(input_url_worker_importScripts);
504 worker.onmessage = this.step_func_done(function(e) {
505 assert_equals(e.data, expected_utf8);
507 }, 'importScripts() in a dedicated worker');
509 subsetTestByKey('workers', async_test, function() {
510 var worker = new Worker(input_url_worker_worker);
511 worker.onmessage = this.step_func_done(function(e) {
512 assert_equals(e.data, expected_utf8);
514 }, 'Worker() in a dedicated worker');
516 subsetTestByKey('workers', async_test, function() {
517 var worker = new SharedWorker(input_url_sharedworker_importScripts);
518 worker.port.onmessage = this.step_func_done(function(e) {
519 assert_equals(e.data, expected_utf8);
521 }, 'importScripts() in a shared worker');
523 subsetTestByKey('workers', async_test, function() {
524 var worker = new SharedWorker(input_url_sharedworker_worker);
525 worker.port.onmessage = this.step_func_done(function(e) {
526 assert_equals(e.data, expected_utf8);
528 }, 'Worker() in a shared worker');
531 subsetTestByKey('websocket', async_test, function() {
532 var ws = new WebSocket('ws://{{host}}:{{ports[ws][0]}}/echo-query?\u00E5');
533 this.add_cleanup(function() {
536 ws.onmessage = this.step_func_done(function(e) {
537 assert_equals(e.data, expected_utf8);
539 }, 'WebSocket constructor');
542 subsetTestByKey('websocket', test, function() {
543 var ws = new WebSocket('ws://{{host}}:{{ports[ws][0]}}/echo-query?\u00E5');
546 assert_true(ws.url.indexOf(expected_utf8) > -1, msg(expected_utf8, got));
550 function test_css(tmpl, expected_cssom, encoding, use_style_element) {
551 var desc = ['CSS', (use_style_element ? '<style>' : '<link> (' + encoding + ')'), tmpl].join(' ');
552 subsetTestByKey('css', async_test, function(){
553 css_is_supported(tmpl, expected_cssom, this);
555 var id = 'test_css_' + uuid;
556 var url = 'url(stash.py?q=%s&action=put&id=' + uuid + ')';
557 tmpl = tmpl.replace(/<id>/g, id).replace(/<url>/g, url);
559 if (use_style_element) {
560 link = document.createElement('style');
561 link.textContent = tmpl.replace(/%s/g, '\u00E5').replace(/stash\.py/g, 'resources/stash.py');
563 link = document.createElement('link');
564 link.rel = 'stylesheet';
565 link.href = 'resources/css-tmpl.py?encoding='+encoding+'&tmpl='+encodeURIComponent(tmpl);
567 var div = document.createElement('div');
570 document.head.appendChild(link);
571 document.body.appendChild(div);
572 this.add_cleanup(function() {
573 document.head.removeChild(link);
574 document.body.removeChild(div);
576 poll_for_stash(this, uuid, expected_utf8);
580 // fail fast if the input doesn't parse into the expected cssom
581 function css_is_supported(tmpl, expected_cssom, test_obj) {
582 if (expected_cssom === null) {
585 var style = document.createElement('style');
586 style.textContent = tmpl.replace(/<id>/g, 'x').replace(/<url>/g, 'url(data:,)');
587 document.head.appendChild(style);
588 test_obj.add_cleanup(function() {
589 document.head.removeChild(style);
591 assert_equals(style.sheet.cssRules.length, expected_cssom.length, 'number of style rules');
592 for (var i = 0; i < expected_cssom.length; ++i) {
593 if (expected_cssom[i] === null) {
596 assert_equals(style.sheet.cssRules[i].style.length, expected_cssom[i], 'number of declarations in style rule #'+i);
600 [['#<id> { background-image:<url> }', [1] ],
601 ['#<id> { border-image-source:<url> }', [1] ],
602 ['#<id>::before { content:<url> }', [1] ],
603 ['@font-face { font-family:<id>; src:<url> } #<id> { font-family:<id> }', [null, 1] ],
604 ['#<id> { display:list-item; list-style-image:<url> }', [2] ],
605 ['@import <url>;', null ],
606 // XXX maybe cursor isn't suitable for automation here if browsers delay fetching it
607 ['#<id> { cursor:<url>, pointer }', [1] ]].forEach(function(arr) {
609 var expected_cssom = arr[1];
610 var other_encoding = encoding == 'utf-8' ? 'windows-1252' : 'utf-8';
611 test_css(input, expected_cssom, encoding);
612 test_css(input, expected_cssom, other_encoding);
613 test_css(input, expected_cssom, null, true);
616 // XXX maybe test if they become relevant:
617 // binding (obsolete?)
618 // aural: cue-after, cue-before, play-during (not implemented?)
619 // hyphenate-resource (not implemented?)
620 // image() (not implemented?)
622 // <?xml-stylesheet?>
623 subsetTestByKey('xml', async_test, function() {
624 var iframe = document.createElement('iframe');
625 iframe.src = input_url_xmlstylesheet_css;
626 document.body.appendChild(iframe);
627 this.add_cleanup(function() {
628 document.body.removeChild(iframe);
630 iframe.onload = this.step_func_done(function() {
631 assert_equals(iframe.contentDocument.firstChild.sheet.cssRules[0].style.content, '"' + expected_utf8 + '"');
633 }, '<?xml-stylesheet?> (CSS)');
636 subsetTestByKey('url', test, function() {
637 var url = new URL('http://example.org/'+input_url);
638 var expected = expected_utf8;
639 assert_true(url.href.indexOf(expected) > -1, 'url.href '+msg(expected, url.href));
640 assert_true(url.search.indexOf(expected) > -1, 'url.search '+msg(expected, url.search));
641 }, 'URL constructor, url');
643 subsetTestByKey('url', test, function() {
644 var url = new URL('', 'http://example.org/'+input_url);
645 var expected = expected_utf8;
646 assert_true(url.href.indexOf(expected) > -1, 'url.href '+msg(expected, url.href));
647 assert_true(url.search.indexOf(expected) > -1, 'url.search '+msg(expected, url.search));
648 }, 'URL constructor, base');
650 // Test different schemes
651 function test_scheme(url, utf8) {
652 subsetTestByKey('scheme', test, function() {
653 var a = document.createElement('a');
654 a.setAttribute('href', url);
656 var expected = utf8 ? expected_utf8 : expected_current;
657 assert_true(got.indexOf(expected) != -1, msg(expected, got));
658 }, 'Scheme ' + url.split(':')[0] + ' (getting <a>.href)');
661 var test_scheme_urls = ['ftp://example.invalid/?x=\u00E5',
663 'http://example.invalid/?x=\u00E5',
664 'https://example.invalid/?x=\u00E5',
667 var test_scheme_urls_utf8 = ['ws://example.invalid/?x=\u00E5',
668 'wss://example.invalid/?x=\u00E5',
669 'gopher://example.invalid/?x=\u00E5',
670 'mailto:example@invalid?x=\u00E5',
671 'data:text/plain;charset='+encoding+',?x=\u00E5',
672 'javascript:"?x=\u00E5"',
673 'ftps://example.invalid/?x=\u00E5',
674 'httpbogus://example.invalid/?x=\u00E5',
675 'bitcoin:foo?x=\u00E5',
680 'magnet:foo?x=\u00E5',
686 'smsto:foo?x=\u00E5',
690 'webcal:foo?x=\u00E5',
693 'web+http:foo?x=\u00E5',
696 test_scheme_urls.forEach(function(url) {
700 test_scheme_urls_utf8.forEach(function(url) {
701 test_scheme(url, true);