Update testharness.js from upstream
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Sep 2019 21:04:08 +0000 (21:04 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Sep 2019 21:04:08 +0000 (21:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201808

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Update existing layout tests so that they are compatible with this new version
of testharness.js.

* IndexedDB-private-browsing/idbdatabase_createObjectStore9-invalidparameters.html:
* IndexedDB-private-browsing/idbobjectstore_deleted.html:
* web-platform-tests/custom-elements/resources/custom-elements-helpers.js:
(create_window_in_test):

LayoutTests:

Update testharness.js from upstream 6fd5e1e086ce590a4780a30d12968.
Update existing layout tests so that they are compatible with this new version
of testharness.js.

* TestExpectations:
* css-custom-properties-api/length-expected.txt:
* css-custom-properties-api/length.html:
* css-custom-properties-api/registerProperty.html:
* css-dark-mode/older-systems/color-scheme-css-expected.txt:
* css-dark-mode/older-systems/color-scheme-css.html:
* css-dark-mode/older-systems/color-scheme-meta-expected.txt:
* css-dark-mode/older-systems/color-scheme-meta.html:
* css3/flexbox/style-change-expected.txt:
* fast/css/DOMQuad-serialization.html:
* fast/css/Element-style.html:
* fast/css/parse-justify-self-expected.txt:
* fast/css/parse-justify-self.html:
* fast/events/clipboard-event-constructor-expected.txt:
* fast/events/clipboard-event-constructor.html:
* fast/media/mq-js-update-media-expected.txt:
* fast/media/mq-js-update-media.html:
* fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt:
* fast/mediastream/captureStream/canvas2d-expected.txt:
* fast/mediastream/captureStream/canvas2d-heavy-drawing.html:
* fast/mediastream/captureStream/canvas2d.html:
* fast/shadow-dom/event-path-with-window-expected.txt:
* fast/shadow-dom/event-path-with-window.html:
* fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt:
* fast/shadow-dom/offsetParent-across-shadow-boundaries.html:
* fast/shadow-dom/slotchange-for-slot-mutation-expected.txt:
* fast/shadow-dom/slotchange-for-slot-mutation.html:
* fast/shadow-dom/stylesheet-title-in-shadow-tree-expected.txt:
* fast/shadow-dom/stylesheet-title-in-shadow-tree.html:
* http/tests/fetch/redirectmode-and-preload-expected.txt:
* http/tests/fetch/redirectmode-and-preload.html:
* imported/blink/editing/selection/selectstart-event-crash-expected.txt:
* js/promises-tests/promises-in-workers-expected.txt:
* js/promises-tests/promises-in-workers.js:
* resources/check-layout-th.js:
(window.checkLayout):
* resources/testharness.js:
(WindowTestEnvironment):
(WindowTestEnvironment.prototype._dispatch):
(WindowTestEnvironment.prototype._forEach_windows):
(WindowTestEnvironment.prototype.next_default_test_name):
(WorkerTestEnvironment.prototype.next_default_test_name):
(ServiceWorkerTestEnvironment.on_all_loaded):
(ServiceWorkerTestEnvironment):
(ShellTestEnvironment):
(ShellTestEnvironment.prototype.next_default_test_name):
(ShellTestEnvironment.prototype.on_new_harness_properties):
(ShellTestEnvironment.prototype.on_tests_ready):
(ShellTestEnvironment.prototype.add_on_loaded_callback):
(ShellTestEnvironment.prototype.test_timeout):
(create_test_environment):
(is_shared_worker):
(is_service_worker):
(test):
(promise_test):
(this.wait_for):
(EventWatcher):
(done):
* streams/readable-byte-stream-controller-expected.txt:
* streams/readable-byte-stream-controller-worker-expected.txt: Added.
* streams/readable-byte-stream-controller-worker.html: Copied from LayoutTests/streams/readable-byte-stream-controller.html.
* streams/readable-byte-stream-controller.html:
* streams/readable-stream-byob-reader-expected.txt:
* streams/readable-stream-byob-reader-worker-expected.txt: Added.
* streams/readable-stream-byob-reader-worker.html: Copied from LayoutTests/streams/readable-stream-byob-reader.html.
* streams/readable-stream-byob-reader.html:
* streams/readable-stream-byob-request-expected.txt:
* streams/readable-stream-byob-request-worker-expected.txt: Copied from LayoutTests/streams/readable-stream-byob-request-expected.txt.
* streams/readable-stream-byob-request-worker.html: Copied from LayoutTests/streams/readable-stream-byob-request.html.
* streams/readable-stream-byob-request.html:
* streams/reference-implementation/readable-stream-templated-expected.txt:
* streams/reference-implementation/readable-stream-templated.html:
* streams/reference-implementation/writable-stream-abort-expected.txt:
* streams/reference-implementation/writable-stream-expected.txt:
* webrtc/datachannel/basic-expected.txt:
* webrtc/datachannel/basic.html:
* webrtc/datachannel/mdns-ice-candidates-expected.txt:
* webrtc/datachannel/mdns-ice-candidates.html:
* webrtc/pc-detached-document-expected.txt:
* webrtc/pc-detached-document.html:
* webrtc/video-mute-expected.txt:
* webrtc/video-mute-vp8-expected.txt:
* webrtc/video-mute-vp8.html:
* webrtc/video-mute.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249918 268f45cc-cd09-0410-ab3c-d52691b4dbfc

68 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/css-custom-properties-api/length-expected.txt
LayoutTests/css-custom-properties-api/length.html
LayoutTests/css-custom-properties-api/registerProperty.html
LayoutTests/css-dark-mode/older-systems/color-scheme-css-expected.txt
LayoutTests/css-dark-mode/older-systems/color-scheme-css.html
LayoutTests/css-dark-mode/older-systems/color-scheme-meta-expected.txt
LayoutTests/css-dark-mode/older-systems/color-scheme-meta.html
LayoutTests/css3/flexbox/style-change-expected.txt
LayoutTests/fast/css/DOMQuad-serialization.html
LayoutTests/fast/css/Element-style.html
LayoutTests/fast/css/parse-justify-self-expected.txt
LayoutTests/fast/css/parse-justify-self.html
LayoutTests/fast/events/clipboard-event-constructor-expected.txt
LayoutTests/fast/events/clipboard-event-constructor.html
LayoutTests/fast/media/mq-js-update-media-expected.txt
LayoutTests/fast/media/mq-js-update-media.html
LayoutTests/fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt
LayoutTests/fast/mediastream/captureStream/canvas2d-heavy-drawing.html
LayoutTests/fast/mediastream/captureStream/canvas2d.html
LayoutTests/fast/shadow-dom/event-path-with-window-expected.txt
LayoutTests/fast/shadow-dom/event-path-with-window.html
LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt
LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries.html
LayoutTests/fast/shadow-dom/slotchange-for-slot-mutation-expected.txt
LayoutTests/fast/shadow-dom/slotchange-for-slot-mutation.html
LayoutTests/fast/shadow-dom/stylesheet-title-in-shadow-tree-expected.txt
LayoutTests/fast/shadow-dom/stylesheet-title-in-shadow-tree.html
LayoutTests/http/tests/fetch/redirectmode-and-preload-expected.txt
LayoutTests/http/tests/fetch/redirectmode-and-preload.html
LayoutTests/imported/blink/editing/selection/selectstart-event-crash-expected.txt
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/IndexedDB-private-browsing/idbdatabase_createObjectStore9-invalidparameters.html
LayoutTests/imported/w3c/IndexedDB-private-browsing/idbobjectstore_deleted.html
LayoutTests/imported/w3c/web-platform-tests/custom-elements/resources/custom-elements-helpers.js
LayoutTests/js/promises-tests/promises-in-workers-expected.txt
LayoutTests/js/promises-tests/promises-in-workers.js
LayoutTests/platform/win/TestExpectations
LayoutTests/platform/wincairo/TestExpectations
LayoutTests/resources/check-layout-th.js
LayoutTests/resources/testharness.js
LayoutTests/streams/readable-byte-stream-controller-expected.txt
LayoutTests/streams/readable-byte-stream-controller-worker-expected.txt [new file with mode: 0644]
LayoutTests/streams/readable-byte-stream-controller-worker.html [new file with mode: 0644]
LayoutTests/streams/readable-byte-stream-controller.html
LayoutTests/streams/readable-stream-byob-reader-expected.txt
LayoutTests/streams/readable-stream-byob-reader-worker-expected.txt [new file with mode: 0644]
LayoutTests/streams/readable-stream-byob-reader-worker.html [new file with mode: 0644]
LayoutTests/streams/readable-stream-byob-reader.html
LayoutTests/streams/readable-stream-byob-request-expected.txt
LayoutTests/streams/readable-stream-byob-request-worker-expected.txt [new file with mode: 0644]
LayoutTests/streams/readable-stream-byob-request-worker.html [new file with mode: 0644]
LayoutTests/streams/readable-stream-byob-request.html
LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt
LayoutTests/streams/reference-implementation/readable-stream-templated.html
LayoutTests/streams/reference-implementation/writable-stream-abort-expected.txt
LayoutTests/streams/reference-implementation/writable-stream-expected.txt
LayoutTests/webrtc/datachannel/basic-expected.txt
LayoutTests/webrtc/datachannel/basic.html
LayoutTests/webrtc/datachannel/mdns-ice-candidates-expected.txt
LayoutTests/webrtc/datachannel/mdns-ice-candidates.html
LayoutTests/webrtc/pc-detached-document-expected.txt
LayoutTests/webrtc/pc-detached-document.html
LayoutTests/webrtc/video-mute-expected.txt
LayoutTests/webrtc/video-mute-vp8-expected.txt
LayoutTests/webrtc/video-mute-vp8.html
LayoutTests/webrtc/video-mute.html

index 6920c2f..b800928 100644 (file)
@@ -1,3 +1,99 @@
+2019-09-16  Chris Dumez  <cdumez@apple.com>
+
+        Update testharness.js from upstream
+        https://bugs.webkit.org/show_bug.cgi?id=201808
+
+        Reviewed by Darin Adler.
+
+        Update testharness.js from upstream 6fd5e1e086ce590a4780a30d12968.
+        Update existing layout tests so that they are compatible with this new version
+        of testharness.js.
+
+        * TestExpectations:
+        * css-custom-properties-api/length-expected.txt:
+        * css-custom-properties-api/length.html:
+        * css-custom-properties-api/registerProperty.html:
+        * css-dark-mode/older-systems/color-scheme-css-expected.txt:
+        * css-dark-mode/older-systems/color-scheme-css.html:
+        * css-dark-mode/older-systems/color-scheme-meta-expected.txt:
+        * css-dark-mode/older-systems/color-scheme-meta.html:
+        * css3/flexbox/style-change-expected.txt:
+        * fast/css/DOMQuad-serialization.html:
+        * fast/css/Element-style.html:
+        * fast/css/parse-justify-self-expected.txt:
+        * fast/css/parse-justify-self.html:
+        * fast/events/clipboard-event-constructor-expected.txt:
+        * fast/events/clipboard-event-constructor.html:
+        * fast/media/mq-js-update-media-expected.txt:
+        * fast/media/mq-js-update-media.html:
+        * fast/mediacapturefromelement/CanvasCaptureMediaStream-offscreencanvas-expected.txt:
+        * fast/mediastream/captureStream/canvas2d-expected.txt:
+        * fast/mediastream/captureStream/canvas2d-heavy-drawing.html:
+        * fast/mediastream/captureStream/canvas2d.html:
+        * fast/shadow-dom/event-path-with-window-expected.txt:
+        * fast/shadow-dom/event-path-with-window.html:
+        * fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt:
+        * fast/shadow-dom/offsetParent-across-shadow-boundaries.html:
+        * fast/shadow-dom/slotchange-for-slot-mutation-expected.txt:
+        * fast/shadow-dom/slotchange-for-slot-mutation.html:
+        * fast/shadow-dom/stylesheet-title-in-shadow-tree-expected.txt:
+        * fast/shadow-dom/stylesheet-title-in-shadow-tree.html:
+        * http/tests/fetch/redirectmode-and-preload-expected.txt:
+        * http/tests/fetch/redirectmode-and-preload.html:
+        * imported/blink/editing/selection/selectstart-event-crash-expected.txt:
+        * js/promises-tests/promises-in-workers-expected.txt:
+        * js/promises-tests/promises-in-workers.js:
+        * resources/check-layout-th.js:
+        (window.checkLayout):
+        * resources/testharness.js:
+        (WindowTestEnvironment):
+        (WindowTestEnvironment.prototype._dispatch):
+        (WindowTestEnvironment.prototype._forEach_windows):
+        (WindowTestEnvironment.prototype.next_default_test_name):
+        (WorkerTestEnvironment.prototype.next_default_test_name):
+        (ServiceWorkerTestEnvironment.on_all_loaded):
+        (ServiceWorkerTestEnvironment):
+        (ShellTestEnvironment):
+        (ShellTestEnvironment.prototype.next_default_test_name):
+        (ShellTestEnvironment.prototype.on_new_harness_properties):
+        (ShellTestEnvironment.prototype.on_tests_ready):
+        (ShellTestEnvironment.prototype.add_on_loaded_callback):
+        (ShellTestEnvironment.prototype.test_timeout):
+        (create_test_environment):
+        (is_shared_worker):
+        (is_service_worker):
+        (test):
+        (promise_test):
+        (this.wait_for):
+        (EventWatcher):
+        (done):
+        * streams/readable-byte-stream-controller-expected.txt:
+        * streams/readable-byte-stream-controller-worker-expected.txt: Added.
+        * streams/readable-byte-stream-controller-worker.html: Copied from LayoutTests/streams/readable-byte-stream-controller.html.
+        * streams/readable-byte-stream-controller.html:
+        * streams/readable-stream-byob-reader-expected.txt:
+        * streams/readable-stream-byob-reader-worker-expected.txt: Added.
+        * streams/readable-stream-byob-reader-worker.html: Copied from LayoutTests/streams/readable-stream-byob-reader.html.
+        * streams/readable-stream-byob-reader.html:
+        * streams/readable-stream-byob-request-expected.txt:
+        * streams/readable-stream-byob-request-worker-expected.txt: Copied from LayoutTests/streams/readable-stream-byob-request-expected.txt.
+        * streams/readable-stream-byob-request-worker.html: Copied from LayoutTests/streams/readable-stream-byob-request.html.
+        * streams/readable-stream-byob-request.html:
+        * streams/reference-implementation/readable-stream-templated-expected.txt:
+        * streams/reference-implementation/readable-stream-templated.html:
+        * streams/reference-implementation/writable-stream-abort-expected.txt:
+        * streams/reference-implementation/writable-stream-expected.txt:
+        * webrtc/datachannel/basic-expected.txt:
+        * webrtc/datachannel/basic.html:
+        * webrtc/datachannel/mdns-ice-candidates-expected.txt:
+        * webrtc/datachannel/mdns-ice-candidates.html:
+        * webrtc/pc-detached-document-expected.txt:
+        * webrtc/pc-detached-document.html:
+        * webrtc/video-mute-expected.txt:
+        * webrtc/video-mute-vp8-expected.txt:
+        * webrtc/video-mute-vp8.html:
+        * webrtc/video-mute.html:
+
 2019-09-16  Russell Epstein  <repstein@apple.com>
 
         [ macOS ] Layout tests webgpu/*-triangle-strip.html are flaky failures.
index a281d9c..0f8ef38 100644 (file)
@@ -3615,6 +3615,9 @@ webkit.org/b/199028 webgpu/whlsl/test-harness-test.html [ Slow ]
 
 imported/w3c/web-platform-tests/css/cssom-view/offsetTopLeft-inline.html [ ImageOnlyFailure ]
 
+# Test is failing and is printing different values when it fails.
+fast/mediastream/captureStream/canvas2d.html [ Failure Pass ]
+
 # These tests only work on systems with the design system ui fonts installed.
 fast/text/design-system-ui-11.html [ ImageOnlyFailure ]
 fast/text/design-system-ui-12.html [ ImageOnlyFailure ]
index d6d106b..388b594 100644 (file)
@@ -25,6 +25,6 @@ PASS JS Attributes are valid for element 2
 PASS JS Attributes are valid for element 3 
 PASS Test that calc is not substituted for unregistered property 
 PASS Test that calc is substituted for custom property with length syntax 
-PASS Test that calc is substituted for custom property with length syntax 
+PASS Test that calc is substituted for custom property with length syntax 
 PASS Test that variables are substituted from JS 
 
index 4549eea..254a068 100644 (file)
@@ -110,7 +110,7 @@ test(function() {
 }, "Test that calc is substituted for custom property with length syntax");
 test(function() {
   test_prop('calcTest3', '--my-custom-prop2', '110px');
-}, "Test that calc is substituted for custom property with length syntax");
+}, "Test that calc is substituted for custom property with length syntax 2");
 test(function() {
   test_prop('subTest', '--my-custom-prop-unreg', '200px');
 }, "Test that variables are substituted from JS");
index 3dbcb0c..2ba92ba 100644 (file)
@@ -31,7 +31,7 @@ test(function() {
     assert_throws(new TypeError(), () => CSS.registerProperty({name: '--no-inherits'}));
     assert_throws(new TypeError(), () => CSS.registerProperty({inherits: false}));
     // Repeated name
-    assert_throws(null,
+    assert_throws(DOMException.INVALID_MODIFICATION_ERR,
         () => CSS.registerProperty({name: '--syntax-test-3', inherits: false, initialValue: '500px'}));
 }, "registerProperty always allows omitting initialValue and syntax, requires name and inherits");
 test(function() {
index e535a16..2c00628 100644 (file)
@@ -5,7 +5,7 @@ PASS Element colors are correct in light color scheme with light and dark suppor
 PASS Color schemes changed to dark 
 PASS Element colors are correct in light color scheme with only dark supported color scheme 
 PASS Color schemes changed to light and a bogus value 
-PASS Element colors are correct in light color scheme with only light supported color scheme 
+PASS Element colors are correct in light color scheme with only light supported color scheme 
 PASS Color schemes changed to auto value 
 PASS Element colors are correct in light color scheme with implicit light supported color scheme 
 PASS Color schemes changed to light and dark via <meta> element 
@@ -13,8 +13,8 @@ PASS Element colors are correct in light color scheme with light and dark suppor
 PASS Color schemes changed to light value 
 PASS Element colors are correct in light color scheme with explicit light, overriding <meta> element 
 PASS Remove test meta element 
-PASS Color schemes changed to light and dark 
-PASS Element colors are correct in light color scheme with light and dark supported color scheme 
+PASS Color schemes changed to light and dark 
+PASS Element colors are correct in light color scheme with light and dark supported color scheme 
 PASS Color schemes changed to a bogus value and dark 
 PASS Element colors are correct in light color scheme with dark supported color scheme 
 
index b548835..c73f34d 100644 (file)
@@ -49,7 +49,7 @@ test(function() {
 test(function() {
     // The semantic text color should be black again.
     test_color_is_black("test1");
-}, "Element colors are correct in light color scheme with only light supported color scheme");
+}, "Element colors are correct in light color scheme with only light supported color scheme 2");
 
 test(function() {
     document.body.style.setProperty("color-scheme", "auto");
@@ -87,11 +87,11 @@ test(function() {
 
 test(function() {
     document.body.style.setProperty("color-scheme", "light dark");
-}, "Color schemes changed to light and dark");
+}, "Color schemes changed to light and dark 2");
 
 test(function() {
     test_color_is_black("test1");
-}, "Element colors are correct in light color scheme with light and dark supported color scheme");
+}, "Element colors are correct in light color scheme with light and dark supported color scheme 2");
 
 test(function() {
     document.body.style.setProperty("color-scheme", "foo dark");
index 38c8405..2ee7405 100644 (file)
@@ -5,11 +5,11 @@ PASS Element colors are correct in light color scheme with light and dark color
 PASS Color schemes changed to dark 
 PASS Element colors are correct in light color scheme with only dark color scheme 
 PASS Color schemes changed to light and a bogus value 
-PASS Element colors are correct in light color scheme with only light color scheme 
+PASS Element colors are correct in light color scheme with only light color scheme 
 PASS Color schemes changed to empty value 
 PASS Element colors are correct in light color scheme with implicit light color scheme 
-PASS Color schemes changed to light and dark 
-PASS Element colors are correct in light color scheme with light and dark color scheme 
+PASS Color schemes changed to light and dark 
+PASS Element colors are correct in light color scheme with light and dark color scheme 
 PASS Color schemes changed to a bogus value and dark 
 PASS Element colors are correct in light color scheme with dark color scheme 
 
index 9790329..b3fb58a 100644 (file)
@@ -48,7 +48,7 @@ test(function() {
 
 test(function() {
     test_color_is_black("test1");
-}, "Element colors are correct in light color scheme with only light color scheme");
+}, "Element colors are correct in light color scheme with only light color scheme 2");
 
 test(function() {
     document.getElementById("meta").content = "";
@@ -60,11 +60,11 @@ test(function() {
 
 test(function() {
     document.getElementById("meta").content = "   light, dark ";
-}, "Color schemes changed to light and dark");
+}, "Color schemes changed to light and dark 2");
 
 test(function() {
     test_color_is_black("test1");
-}, "Element colors are correct in light color scheme with light and dark color scheme");
+}, "Element colors are correct in light color scheme with light and dark color scheme 2");
 
 test(function() {
     document.getElementById("meta").content = ", foo dark";
index 948563c..b6fd9e4 100644 (file)
@@ -1,10 +1,10 @@
 
 PASS .flexbox 1 
-PASS .flexbox 1 
-PASS .flexbox 1 
-PASS .flexbox 1 
-PASS .flexbox 1 
-PASS .flexbox 1 
+PASS .flexbox 2 
+PASS .flexbox 3 
+PASS .flexbox 4 
+PASS .flexbox 5 
+PASS .flexbox 6 
 This test verifies that changing order, align-content, align-items, align-self, or justify-content will relayout.
 
 
index c960825..9324763 100644 (file)
@@ -11,7 +11,7 @@ test(() => {
     assert_equals(quad.__proto__, DOMQuad.prototype);
 
     var toJSONDescriptor = Object.getOwnPropertyDescriptor(DOMQuad.prototype, "toJSON");
-    assert_exists(toJSONDescriptor, "value");
+    assert_own_property(toJSONDescriptor, "value");
     assert_true(toJSONDescriptor.enumerable);
     assert_true(toJSONDescriptor.configurable);
     assert_true(toJSONDescriptor.writable);
index 02b9bce..11f3491 100644 (file)
@@ -17,7 +17,7 @@ test(() => {
     assert_idl_attribute(testSVG, "style", "SVGElement has 'style' attribute");
     assert_own_property(SVGElement.prototype, "style", "'style' is on SVGElement prototype");
 
-    assert_not_exists("Element.prototype", "style", "'style' is not on Element prototype");
+    assert_not_own_property("Element.prototype", "style", "'style' is not on Element prototype");
 }, "'style' property location");
 
 test(() => {
index 5d5811d..14e59c2 100644 (file)
@@ -11,6 +11,6 @@ PASS Test the value 'initial' for grid containers
 PASS Test the value 'initial' for flex containers 
 PASS Test the value 'initial' for positioned elements 
 PASS Test the value 'initial' for positioned elements in grid containers 
-PASS Test the value 'initial' for positioned elements in grid containers 
+PASS Test the value 'initial' for positioned elements in grid containers 
 PASS Test the value 'inherit' 
 
index 7d0cc1b..476f5f4 100644 (file)
@@ -287,7 +287,7 @@ test(function() {
     container.style.display = "flex";
     element.style.position = "absolute";
     checkInitialValues(element, "justifySelf", "justify-self", "end", "auto");
-}, "Test the value 'initial' for positioned elements in grid containers");
+}, "Test the value 'initial' for positioned elements in grid containers 2");
 
 test(function() {
     checkInheritValues("justifySelf", "justify-self", "end");
index 2a1792b..ed36406 100644 (file)
@@ -2,8 +2,8 @@
 PASS First parameter is mandatory 
 PASS Omit init dictionary 
 PASS bubbles member 
-PASS bubbles member 
-PASS bubbles member 
-PASS bubbles member 
+PASS cancelable member 
+PASS composed member 
+PASS bubbles / cancelable / composed members 
 PASS Passing a bad type for clipboardData member should throw 
 
index 92c5735..7749361 100644 (file)
@@ -41,7 +41,7 @@ test(function() {
     assert_equals(event.cancelable, true);
     assert_equals(event.composed, false);
     assert_equals(event.clipboardData, null);
-}, "bubbles member");
+}, "cancelable member");
 
 test(function() {
     var event = new ClipboardEvent("foo", {composed: true});
@@ -51,7 +51,7 @@ test(function() {
     assert_equals(event.cancelable, false);
     assert_equals(event.composed, true);
     assert_equals(event.clipboardData, null);
-}, "bubbles member");
+}, "composed member");
 
 test(function() {
     var event = new ClipboardEvent("foo", {bubbles: true, cancelable: true, composed: true});
@@ -61,7 +61,7 @@ test(function() {
     assert_equals(event.cancelable, true);
     assert_equals(event.composed, true);
     assert_equals(event.clipboardData, null);
-}, "bubbles member");
+}, "bubbles / cancelable / composed members");
 
 test(function() {
     assert_throws(new TypeError(), function() {
index 51fd5d8..d60e19a 100644 (file)
@@ -1,7 +1,7 @@
 This test verifies that when media query is updated, style is recalculated.
 
-PASS Testing that background color is red. Media query "all and (color)" 
-PASS Testing that updated media query doesn't match and background color is not red. Media query "(monochrome) and (color)" 
-PASS Testing that background color is red. Media query "all and (color)" 
-PASS Testing that updated media query doesn't match and background color is not red. Media query "(monochrome) and (color)" 
+PASS Testing that background color is red. Media query "all and (color)" 
+PASS Testing that updated media query doesn't match and background color is not red. Media query "(monochrome) and (color)" 
+PASS Testing that background color is red. Media query "all and (color)" 
+PASS Testing that updated media query doesn't match and background color is not red. Media query "(monochrome) and (color)" 
 
index c94efcb..d87d4c7 100644 (file)
@@ -8,7 +8,7 @@
     <script src="../../resources/testharnessreport.js"></script>
     <script type="text/javascript">
       setup({ "explicit_done": true });
-      function updateMediaQuery() {
+      function updateMediaQuery(id) {
           var styleElement = document.getElementById("styleElement");
           var divElement = document.getElementById("divElement");
           var divComputedStyle = window.getComputedStyle(divElement);
@@ -17,7 +17,7 @@
                    assert_true(divComputedStyle.getPropertyCSSValue('background-color').getRGBColorValue().red.getFloatValue(CSSPrimitiveValue.CSS_NUMBER) == 255,
                                "Div should have rgb(255, 0, 0) background color.")
                },
-               "Testing that background color is red. Media query \"all and (color)\"");
+               "Testing that background color is red. Media query \"all and (color)\" " + id);
 
           // update media attribute, background-color should not be red
           styleElement.setAttribute("media", "(monochrome) and (color)");
                    assert_true(divComputedStyle.getPropertyCSSValue('background-color').getRGBColorValue().red.getFloatValue(CSSPrimitiveValue.CSS_NUMBER) == 0,
                                "New media query doesn't match, div should not have background color.")
                },
-               "Testing that updated media query doesn't match and background color is not red. Media query \"(monochrome) and (color)\"");
+               "Testing that updated media query doesn't match and background color is not red. Media query \"(monochrome) and (color)\" " + id);
 
            // reset media query to original
            styleElement.setAttribute("media", "all and (color)");
       }
     </script>
 </head>
-  <body onload="updateMediaQuery(); done();">
+  <body onload="updateMediaQuery(2); done();">
     <span>This test verifies that when media query is updated, style is recalculated.</span>
     <div id="log"></div>
     <div id="divElement"\>
     <script>
       // update media query while document is parsing
-      updateMediaQuery();
+      updateMediaQuery(1);
     </script>
   </body>
 </html>
index f1bc2cf..abd7642 100644 (file)
@@ -1,4 +1,4 @@
 
-FAIL Untitled canvas.transferControlToOffscreen is not a function. (In 'canvas.transferControlToOffscreen()', 'canvas.transferControlToOffscreen' is undefined)
-FAIL Untitled 1 canvas.transferControlToOffscreen is not a function. (In 'canvas.transferControlToOffscreen()', 'canvas.transferControlToOffscreen' is undefined)
+FAIL CanvasCaptureMediaStream-offscreencanvas canvas.transferControlToOffscreen is not a function. (In 'canvas.transferControlToOffscreen()', 'canvas.transferControlToOffscreen' is undefined)
+FAIL CanvasCaptureMediaStream-offscreencanvas 1 canvas.transferControlToOffscreen is not a function. (In 'canvas.transferControlToOffscreen()', 'canvas.transferControlToOffscreen' is undefined)
 
index 0b40696..5438b4b 100644 (file)
@@ -21,7 +21,7 @@ function validateCanvas(resolve, reject)
              previousTrackSampleCount = internals.trackVideoSampleCount;
         }
         canvas2.getContext("2d").drawImage(video, 0 ,0);
-        assert_array_equals(canvas2.getContext("2d").getImageData(0 ,0, 100, 100), canvas2.getContext("2d").getImageData(0, 0, 100, 100));
+        assert_array_equals(canvas2.getContext("2d").getImageData(0 ,0, 100, 100).data, canvas2.getContext("2d").getImageData(0, 0, 100, 100).data);
     } catch(e) {
         reject(e);
         return;
index 12ef993..82ecaf3 100644 (file)
@@ -19,7 +19,7 @@ function checkCanvas(canvas, stream)
         video.onplay = () => {
             canvas2.getContext("2d").drawImage(video, 0 ,0);
             try {
-                assert_array_equals(canvas.getContext("2d").getImageData(0 ,0, 100, 100), canvas2.getContext("2d").getImageData(0, 0, 100, 100));
+                assert_array_equals(canvas.getContext("2d").getImageData(0 ,0, 100, 100).data, canvas2.getContext("2d").getImageData(0, 0, 100, 100).data);
             } catch(e) {
                 reject(e);
                 return;
index 63b392b..cdc6d28 100644 (file)
@@ -1,8 +1,8 @@
 
-PASS The event must propagate out of open mode shadow boundaries when the composed flag is set 
-PASS The event must propagate out of closed mode shadow boundaries when the composed flag is set 
-PASS The event must propagate out of open mode shadow boundaries when the composed flag is set 
-PASS The event must propagate out of closed mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of open mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of closed mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of open mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of closed mode shadow boundaries when the composed flag is set 
 PASS The event must not propagate out of open mode shadow tree in which the relative target and the relative related target are the same 
 PASS The event must not propagate out of closed mode shadow tree in which the relative target and the relative related target are the same 
 
index 1cae447..4bdfc90 100644 (file)
@@ -37,13 +37,13 @@ function testEventTargetAfterDispatching(mode) {
 
         const expectedPath = ['A1a', 'A1-SR'];
         assert_array_equals(log.eventPath, expectedPath);
-        assert_array_equals(log.eventPath.length, log.pathAtTargets.length);
+        assert_equals(log.eventPath.length, log.pathAtTargets.length);
         assert_array_equals(log.pathAtTargets[0], expectedPath);
         assert_array_equals(log.pathAtTargets[1], expectedPath);
 
         assert_equals(log.event.target, nodes.A1a);
         assert_equals(log.event.currentTarget, null);
-    }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set');
+    }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set 1');
 }
 
 testEventTargetAfterDispatching('open');
@@ -71,7 +71,7 @@ function testComposedEventInDocument(mode) {
         let expectedPath = ['A1a', 'A1-SR', 'A1', 'A-SR', 'A', 'body', 'html', 'document', 'window'];
 
         assert_array_equals(log.eventPath, expectedPath);
-        assert_array_equals(log.eventPath.length, log.pathAtTargets.length);
+        assert_equals(log.eventPath.length, log.pathAtTargets.length);
         assert_array_equals(log.pathAtTargets[0], expectedPath);
         assert_array_equals(log.pathAtTargets[1], expectedPath);
         if (mode != 'open')
@@ -87,7 +87,7 @@ function testComposedEventInDocument(mode) {
 
         assert_equals(log.event.target, nodes.A);
         assert_equals(log.event.currentTarget, null);
-    }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set');
+    }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set 2');
 }
 
 testComposedEventInDocument('open');
@@ -115,7 +115,7 @@ function testComposedEventWithRelatedTargetInDocument(mode) {
         let expectedPath = ['A1a', 'A1-SR', 'A1', 'A-SR'];
 
         assert_array_equals(log.eventPath, expectedPath);
-        assert_array_equals(log.eventPath.length, log.pathAtTargets.length);
+        assert_equals(log.eventPath.length, log.pathAtTargets.length);
         assert_array_equals(log.pathAtTargets[0], expectedPath);
         assert_array_equals(log.pathAtTargets[1], expectedPath);
         if (mode != 'open')
index bc2ce56..df4a285 100644 (file)
@@ -5,8 +5,8 @@ PASS offsetParent must return the offset parent in the same shadow tree of open
 PASS offsetParent must return the offset parent in the same shadow tree of closed mode even when nested 
 PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
 PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
-PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
-PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
+PASS offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
+PASS offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
 PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of open mode 
 PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of closed mode 
 PASS offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of open mode did not have any offset parent 
index c05147a..af5c0d0 100644 (file)
@@ -88,14 +88,14 @@ function testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents(mode)
         const previousBlock = document.createElement('div');
         previousBlock.style.height = '12px';
         container.append(previousBlock, host);
-        this.add_cleanup(() => container.innerHTML = '');
+        this.add_cleanup(() => { container.innerHTML = ''; });
         const shadowRoot = host.attachShadow({mode});
         shadowRoot.innerHTML = '<section style="position: relative; margin-left: 20px; margin-top: 100px; background: #ccc"><div style="position: absolute; top: 10px; left: 10px;"><slot></slot></div></section>';
         const target = host.querySelector('#target');
         assert_equals(target.offsetParent, container);
         assert_equals(target.offsetLeft, 30);
         assert_equals(target.offsetTop, 122);
-    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
+    }, `offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
 }
 
 testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('open');
index 7b64ad5..2024f8b 100644 (file)
@@ -171,18 +171,18 @@ PASS slotchange event should fire on the first default slot removed in the tree
 PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a disconnected closed mode shadow root 
 PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a connected open mode shadow root 
 PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a disconnected open mode shadow root 
-PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a connected closed mode shadow root 
-PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a disconnected closed mode shadow root 
-PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a connected open mode shadow root 
-PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a disconnected open mode shadow root 
+PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a connected closed mode shadow root 
+PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a disconnected closed mode shadow root 
+PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a connected open mode shadow root 
+PASS slotchange event should fire on the first default slot removed in the tree order when replacing all children in a disconnected open mode shadow root 
 PASS slotchange event should fire on the first named slot of the same name removed in the tree order in a connected closed mode shadow root 
 PASS slotchange event should fire on the first named slot of the same name removed in the tree order in a disconnected closed mode shadow root 
 PASS slotchange event should fire on the first named slot of the same name removed in the tree order in a connected open mode shadow root 
 PASS slotchange event should fire on the first named slot of the same name removed in the tree order in a disconnected open mode shadow root 
-PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a connected closed mode shadow root 
-PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a disconnected closed mode shadow root 
-PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a connected open mode shadow root 
-PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a disconnected open mode shadow root 
+PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a connected closed mode shadow root 
+PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a disconnected closed mode shadow root 
+PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a connected open mode shadow root 
+PASS slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order in a disconnected open mode shadow root 
 PASS slotchange event should fire on all first slots of its kind with assigned nodes and their previously-first counterparts in the tree order in a connected closed mode shadow root 
 PASS slotchange event should fire on all first slots of its kind with assigned nodes and their previously-first counterparts in the tree order in a disconnected closed mode shadow root 
 PASS slotchange event should fire on all first slots of its kind with assigned nodes and their previously-first counterparts in the tree order in a connected open mode shadow root 
index fed3e0f..015d3b7 100644 (file)
@@ -239,7 +239,7 @@ generateTests('<span></span>', '<div><p><slot id="slot1"></slot><b><slot id="slo
 
 generateTests('<span></span>', '<div><p><slot id="slot1"></slot><b><slot id="slot2"></slot></b><slot id="slot3"></slot></p></div>',
     (shadowRoot) => shadowRoot.querySelector('p').textContent = '', ['slot1'],
-    'slotchange event should fire on the first default slot removed in the tree order when replacing all children');
+    'slotchange event should fire on the first default slot removed in the tree order when replacing all children 2');
 
 generateTests('<span slot="b"></span>',
     '<p><slot id="slot1" name="a"></slot><b><slot id="slot2" name="b"></slot></b><slot id="slot3" name="b"></slot></p>',
@@ -248,7 +248,7 @@ generateTests('<span slot="b"></span>',
 
 generateTests('<span slot="a"></span><span slot="b"></span>', '<slot id="a1" name="a"></slot><slot id="a2" name="a"></slot><slot></slot>',
     (shadowRoot) => shadowRoot.getElementById('a1').remove(), ['a2', 'a1'],
-    'slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order');
+    'slotchange event should fire when inserting a named slot element before an existing slot of the same name before a default slot in the tree order 2');
 
 generateTests('<span></span><span slot="a"></span>', '<span><slot id="default1"></slot><b><slot id="b1" name="b"></slot>'
     + '<slot id="a1" name="a"></slot></b></span><slot id="default2"></slot><slot id="a2" name="a"></slot>',
index bb69d93..74af097 100644 (file)
@@ -5,10 +5,10 @@ PASS Setting title content attribute on a HTML style element should not set the
 PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
 PASS title content attribute on a SVG style element should not set the title of a stylesheet inside a shadow tree of open mode 
 PASS title content attribute on a SVG style element should not set the title of a stylesheet inside a shadow tree of closed mode 
-PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of open mode 
-PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
-PASS title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of open mode 
-PASS title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
-PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of open mode 
-PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
+PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of open mode 
+PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
+PASS title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of open mode 
+PASS title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
+PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of open mode 
+PASS Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of closed mode 
 
index 86c8f37..934c4c2 100644 (file)
@@ -80,7 +80,7 @@ function testUpdatingTitleOnSVGStyleElemenInShadowTree(mode) {
         assert_equals(shadowRoot.styleSheets[0].title, null);
         assert_equals(shadowRoot.styleSheets[1].title, null);
         assert_equals(getComputedStyle(shadowRoot.querySelector('div')).height, '20px');
-    }, `Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of ${mode} mode`);
+    }, `Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of ${mode} mode 2`);
 }
 
 testUpdatingTitleOnSVGStyleElemenInShadowTree('open');
@@ -100,7 +100,7 @@ function testLinkElementWithTitleInShadowTree(mode) {
         assert_equals(shadowRoot.styleSheets[0].title, null);
         assert_equals(shadowRoot.styleSheets[1].title, null);
         assert_equals(getComputedStyle(shadowRoot.querySelector('div')).backgroundColor, 'rgb(0, 128, 0)');
-    }, `title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of ${mode} mode`);
+    }, `title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of ${mode} mode 2`);
 }
 
 testLinkElementWithTitleInShadowTree('open');
@@ -125,7 +125,7 @@ function testUpdatingTitleOnLinkElementInShadowTree(mode) {
         assert_equals(shadowRoot.styleSheets[0].title, null);
         assert_equals(shadowRoot.styleSheets[1].title, null);
         assert_equals(getComputedStyle(shadowRoot.querySelector('div')).backgroundColor, 'rgb(0, 128, 0)');
-    }, `Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of ${mode} mode`);
+    }, `Setting title content attribute on a HTML style element should not set the title of a stylesheet inside a shadow tree of ${mode} mode 3`);
 }
 
 testUpdatingTitleOnLinkElementInShadowTree('open');
index 0b8c04c..479d8d7 100644 (file)
@@ -2,5 +2,5 @@ CONSOLE MESSAGE: Not allowed to follow a redirection while loading http://127.0.
 CONSOLE MESSAGE: Fetch API cannot load http://127.0.0.1:8000/fetch/resources/redirect-with-cache.php?enableCaching&url=http://localhost:8000/security/resources/allow-if-origin.php?allowCache&origin=http%3A%2F%2F127.0.0.1%3A8000&name=alert-fail.js&contentType=text/ascii due to access control checks.
 
 PASS Fetch should check for redirections even if resource is preloaded (different fetch mode, different redirect mode) 
-PASS Fetch should check for redirections even if resource is preloaded (different fetch mode, different redirect mode) 
+PASS Fetch should check for redirections even if resource is preloaded (different fetch mode, different redirect mode) 
 
index 8bcd9b4..318c14c 100644 (file)
@@ -27,7 +27,7 @@ function startTests()
 
     promise_test(function(test) {
         return promise_rejects(test, new TypeError(), fetch(preloadUrl, {redirect: "error", mode: "cors", credentials: "include"}));
-    }, "Fetch should check for redirections even if resource is preloaded (different fetch mode, different redirect mode)");
+    }, "Fetch should check for redirections even if resource is preloaded (different fetch mode, different redirect mode) 2");
 }
     </script>
   </body>
index 4731414..21cd8a9 100644 (file)
@@ -1,5 +1,20 @@
 2019-09-16  Chris Dumez  <cdumez@apple.com>
 
+        Update testharness.js from upstream
+        https://bugs.webkit.org/show_bug.cgi?id=201808
+
+        Reviewed by Darin Adler.
+
+        Update existing layout tests so that they are compatible with this new version
+        of testharness.js.
+
+        * IndexedDB-private-browsing/idbdatabase_createObjectStore9-invalidparameters.html:
+        * IndexedDB-private-browsing/idbobjectstore_deleted.html:
+        * web-platform-tests/custom-elements/resources/custom-elements-helpers.js:
+        (create_window_in_test):
+
+2019-09-16  Chris Dumez  <cdumez@apple.com>
+
         Unreviewed, land expectations for imported/w3c/web-platform-tests/html/browsers/offline/application-cache-api/api_swapcache_error.https.html.
 
         This test was imported in r249886.
index a5625e2..b67f1ef 100644 (file)
@@ -7,11 +7,11 @@
 <script src=support.js></script>
 
 <script>
-    function invalid_optionalParameters(desc, params) {
+    function invalid_optionalParameters(desc, params, exception) {
         var t = async_test(document.title + " - " + desc);
 
         createdb(t).onupgradeneeded = function(e) {
-            assert_throws(null, function() {
+            assert_throws(exception, function() {
                 e.target.result.createObjectStore("store", params);
             });
 
         };
     }
 
-    invalid_optionalParameters("autoInc and empty keyPath", {autoIncrement: true, keyPath: ""});
-    invalid_optionalParameters("autoInc and keyPath array", {autoIncrement: true, keyPath: []});
-    invalid_optionalParameters("autoInc and keyPath array 2", {autoIncrement: true, keyPath: ["hey"]});
-    invalid_optionalParameters("autoInc and keyPath object", {autoIncrement: true, keyPath: {a:"hey", b:2}});
+    invalid_optionalParameters("autoInc and empty keyPath", {autoIncrement: true, keyPath: ""}, DOMException.INVALID_ACCESS_ERR);
+    invalid_optionalParameters("autoInc and keyPath array", {autoIncrement: true, keyPath: []}, DOMException.SYNTAX_ERR);
+    invalid_optionalParameters("autoInc and keyPath array 2", {autoIncrement: true, keyPath: ["hey"]}, DOMException.INVALID_ACCESS_ERR);
+    invalid_optionalParameters("autoInc and keyPath object", {autoIncrement: true, keyPath: {a:"hey", b:2}}, DOMException.SYNTAX_ERR);
 
 </script>
 
index 8b8f740..ce72bba 100644 (file)
         db.deleteObjectStore("store");
         assert_equals(db.objectStoreNames.length, 0, "objectStoreNames.length after delete");
 
-        assert_throws(null, function() { objStore.add(2); });
-        assert_throws(null, function() { objStore.put(3); });
-        assert_throws(null, function() { objStore.get(1); });
-        assert_throws(null, function() { objStore.clear(); });
-        assert_throws(null, function() { objStore.count(); });
-        assert_throws(null, function() { objStore.delete(1); });
-        assert_throws(null, function() { objStore.openCursor(); });
-        assert_throws(null, function() { objStore.index("idx"); });
-        assert_throws(null, function() { objStore.deleteIndex("idx"); });
-        assert_throws(null, function() { objStore.createIndex("idx2", "a"); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.add(2); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.put(3); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.get(1); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.clear(); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.count(); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.delete(1); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.openCursor(); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.index("idx"); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.deleteIndex("idx"); });
+        assert_throws(DOMException.INVALID_STATE_ERR, function() { objStore.createIndex("idx2", "a"); });
     }
 
     open_rq.onsuccess = function() {
index 015a698..6f73fe2 100644 (file)
@@ -4,7 +4,7 @@ function create_window_in_test(t, srcdoc) {
     f.srcdoc = srcdoc ? srcdoc : '';
     f.onload = (event) => {
       let w = f.contentWindow;
-      t.add_cleanup(() => f.parentNode && f.remove());
+      t.add_cleanup(() => f.remove());
       resolve(w);
     };
     document.body.appendChild(f);
index fa727d8..23c49a8 100644 (file)
@@ -15,7 +15,7 @@ for (let i = 0; i < 2; i++) {
 
     return new Promise(resolve => step_timeout(resolve, 500));
 
-  }, 'Test');
+  }, 'Test ' + (i + 1));
 }
 
 done();
index 1056448..7503ff4 100644 (file)
@@ -3342,6 +3342,7 @@ pageoverlay/overlay-large-document.html [ Failure ]
 security/contentSecurityPolicy/plugins-types-allows-youtube-plugin-replacement.html [ Failure ]
 storage/indexeddb/modern/blob-cursor.html [ Failure ]
 streams/readable-byte-stream-controller.html [ Failure ]
+streams/readable-byte-stream-controller-worker.html [ Failure ]
 svg/W3C-SVG-1.1/struct-dom-03-b.svg [ Failure ]
 svg/W3C-SVG-1.1/struct-dom-04-b.svg [ Failure ]
 svg/W3C-SVG-1.1/struct-dom-05-b.svg [ Failure ]
@@ -3593,7 +3594,9 @@ storage/indexeddb/version-change-event-basic.html [ Failure ]
 storage/indexeddb/wasm-exceptions.html [ Failure ]
 streams/pipe-to.html [ Failure ]
 streams/readable-stream-byob-reader.html [ Failure ]
+streams/readable-stream-byob-reader-worker.html [ Failure ]
 streams/readable-stream-byob-request.html [ Failure ]
+streams/readable-stream-byob-request-worker.html [ Failure ]
 streams/reference-implementation/bad-strategies.html [ Failure ]
 streams/reference-implementation/bad-underlying-sinks.html [ Failure ]
 streams/reference-implementation/brand-checks.html [ Failure ]
index 441ac51..abd8cc9 100644 (file)
@@ -552,8 +552,11 @@ imported/blink/plugins/empty-per-context-data.html [ Skip ]
 
 # ReadableByteStreamController is not implemented
 streams/readable-byte-stream-controller.html [ Skip ]
+streams/readable-byte-stream-controller-worker.html [ Skip ]
 streams/readable-stream-byob-reader.html [ Skip ]
+streams/readable-stream-byob-reader-worker.html [ Skip ]
 streams/readable-stream-byob-request.html [ Skip ]
+streams/readable-stream-byob-request-worker.html [ Skip ]
 
 # image diffs off by 0.0x%
 css2.1/20110323/border-conflict-element-021a.htm [ ImageOnlyFailure ]
index 3f257d4..6ec70a2 100644 (file)
@@ -161,6 +161,8 @@ function checkExpectedValues(t, node, prefix)
     return output.checked;
 }
 
+let testNumbers = new Map();
+
 window.checkLayout = function(selectorList, outputContainer)
 {
     if (!selectorList) {
@@ -168,7 +170,11 @@ window.checkLayout = function(selectorList, outputContainer)
         return;
     }
     var nodes = document.querySelectorAll(selectorList);
-    var testNumber = 0;
+
+    let testNumber = 0;
+    if (testNumbers.has("" + selectorList))
+        testNumber = testNumbers.get("" + selectorList);
+
     nodes = Array.prototype.slice.call(nodes);
     nodes.reverse();
     var checkedLayout = false;
@@ -189,6 +195,9 @@ window.checkLayout = function(selectorList, outputContainer)
     if (!checkedLayout) {
         console.error("No valid data-* attributes found in selector list : " + selectorList);
     }
+
+    testNumbers.set("" + selectorList, testNumber);
+
     done();
 };
 
index 20318b0..1559104 100644 (file)
@@ -10,9 +10,10 @@ policies and contribution forms [3].
 [3] http://www.w3.org/2004/10/27-testcases
 */
 
-/* Documentation is in docs/api.md */
+/* Documentation: https://web-platform-tests.org/writing-tests/testharness-api.html
+ * (../docs/_writing-tests/testharness-api.md) */
 
-(function ()
+(function (global_scope)
 {
     var debug = false;
     // default timeout is 10 seconds, test can override if needed
@@ -47,9 +48,6 @@ policies and contribution forms [3].
      *
      *   // Should return the test harness timeout duration in milliseconds.
      *   float test_timeout();
-     *
-     *   // Should return the global scope object.
-     *   object global_scope();
      * };
      */
 
@@ -66,6 +64,7 @@ policies and contribution forms [3].
         this.all_loaded = false;
         var this_obj = this;
         this.message_events = [];
+        this.dispatched_messages = [];
 
         this.message_functions = {
             start: [add_start_callback, remove_start_callback,
@@ -102,9 +101,23 @@ policies and contribution forms [3].
         on_event(window, 'load', function() {
             this_obj.all_loaded = true;
         });
+
+        on_event(window, 'message', function(event) {
+            if (event.data && event.data.type === "getmessages" && event.source) {
+                // A window can post "getmessages" to receive a duplicate of every
+                // message posted by this environment so far. This allows subscribers
+                // from fetch_tests_from_window to 'catch up' to the current state of
+                // this environment.
+                for (var i = 0; i < this_obj.dispatched_messages.length; ++i)
+                {
+                    event.source.postMessage(this_obj.dispatched_messages[i], "*");
+                }
+            }
+        });
     }
 
     WindowTestEnvironment.prototype._dispatch = function(selector, callback_args, message_arg) {
+        this.dispatched_messages.push(message_arg);
         this._forEach_windows(
                 function(w, same_origin) {
                     if (same_origin) {
@@ -132,9 +145,9 @@ policies and contribution forms [3].
     };
 
     WindowTestEnvironment.prototype._forEach_windows = function(callback) {
-        // Iterate of the the windows [self ... top, opener]. The callback is passed
-        // two objects, the first one is the windows object itself, the second one
-        // is a boolean indicating whether or not its on the same origin as the
+        // Iterate over the windows [self ... top, opener]. The callback is passed
+        // two objects, the first one is the window object itself, the second one
+        // is a boolean indicating whether or not it's on the same origin as the
         // current window.
         var cache = this.window_cache;
         if (!cache) {
@@ -142,33 +155,14 @@ policies and contribution forms [3].
             var w = self;
             var i = 0;
             var so;
-            var origins = location.ancestorOrigins;
             while (w != w.parent) {
                 w = w.parent;
-                // In WebKit, calls to parent windows' properties that aren't on the same
-                // origin cause an error message to be displayed in the error console but
-                // don't throw an exception. This is a deviation from the current HTML5
-                // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504
-                // The problem with WebKit's behavior is that it pollutes the error console
-                // with error messages that can't be caught.
-                //
-                // This issue can be mitigated by relying on the (for now) proprietary
-                // `location.ancestorOrigins` property which returns an ordered list of
-                // the origins of enclosing windows. See:
-                // http://trac.webkit.org/changeset/113945.
-                if (origins) {
-                    so = (location.origin == origins[i]);
-                } else {
-                    so = is_same_origin(w);
-                }
+                so = is_same_origin(w);
                 cache.push([w, so]);
                 i++;
             }
             w = window.opener;
             if (w) {
-                // window.opener isn't included in the `location.ancestorOrigins` prop.
-                // We'll just have to deal with a simple check and an error msg on WebKit
-                // browsers in this case.
                 cache.push([w, is_same_origin(w)]);
             }
             this.window_cache = cache;
@@ -219,12 +213,9 @@ policies and contribution forms [3].
     }
 
     WindowTestEnvironment.prototype.next_default_test_name = function() {
-        //Don't use document.title to work around an Opera bug in XHTML documents
-        var title = document.getElementsByTagName("title")[0];
-        var prefix = (title && title.firstChild && title.firstChild.data) || "Untitled";
         var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
         this.name_counter++;
-        return prefix + suffix;
+        return get_title() + suffix;
     };
 
     WindowTestEnvironment.prototype.on_new_harness_properties = function(properties) {
@@ -251,10 +242,6 @@ policies and contribution forms [3].
         return settings.harness_timeout.normal;
     };
 
-    WindowTestEnvironment.prototype.global_scope = function() {
-        return window;
-    };
-
     /*
      * Base TestEnvironment implementation for a generic web worker.
      *
@@ -298,7 +285,7 @@ policies and contribution forms [3].
     WorkerTestEnvironment.prototype.next_default_test_name = function() {
         var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
         this.name_counter++;
-        return "Untitled" + suffix;
+        return get_title() + suffix;
     };
 
     WorkerTestEnvironment.prototype.on_new_harness_properties = function() {};
@@ -347,10 +334,6 @@ policies and contribution forms [3].
         return null;
     };
 
-    WorkerTestEnvironment.prototype.global_scope = function() {
-        return self;
-    };
-
     /*
      * Dedicated web workers.
      * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope
@@ -414,7 +397,7 @@ policies and contribution forms [3].
         var this_obj = this;
         self.addEventListener("message",
                 function(event) {
-                    if (event.data.type && event.data.type === "connect") {
+                    if (event.data && event.data.type && event.data.type === "connect") {
                         if (event.ports && event.ports[0]) {
                             // If a MessageChannel was passed, then use it to
                             // send results back to the main window.  This
@@ -436,15 +419,25 @@ policies and contribution forms [3].
         // all imported scripts have been fetched and executed. It's the
         // equivalent of an onload event for a document. All tests should have
         // been added by the time this event is received, thus it's not
-        // necessary to wait until the onactivate event.
-        on_event(self, "install",
-                function(event) {
-                    this_obj.all_loaded = true;
-                    if (this_obj.on_loaded_callback) {
-                        this_obj.on_loaded_callback();
-                    }
-                });
+        // necessary to wait until the onactivate event. However, tests for
+        // installed service workers need another event which is equivalent to
+        // the onload event because oninstall is fired only on installation. The
+        // onmessage event is used for that purpose since tests using
+        // testharness.js should ask the result to its service worker by
+        // PostMessage. If the onmessage event is triggered on the service
+        // worker's context, that means the worker's script has been evaluated.
+        on_event(self, "install", on_all_loaded);
+        on_event(self, "message", on_all_loaded);
+        function on_all_loaded() {
+            if (this_obj.all_loaded)
+                return;
+            this_obj.all_loaded = true;
+            if (this_obj.on_loaded_callback) {
+              this_obj.on_loaded_callback();
+            }
+        }
     }
+
     ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
 
     ServiceWorkerTestEnvironment.prototype.add_on_loaded_callback = function(callback) {
@@ -455,50 +448,115 @@ policies and contribution forms [3].
         }
     };
 
+    /*
+     * JavaScript shells.
+     *
+     * This class is used as the test_environment when testharness is running
+     * inside a JavaScript shell.
+     */
+    function ShellTestEnvironment() {
+        this.name_counter = 0;
+        this.all_loaded = false;
+        this.on_loaded_callback = null;
+        Promise.resolve().then(function() {
+            this.all_loaded = true
+            if (this.on_loaded_callback) {
+                this.on_loaded_callback();
+            }
+        }.bind(this));
+        this.message_list = [];
+        this.message_ports = [];
+    }
+
+    ShellTestEnvironment.prototype.next_default_test_name = function() {
+        var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
+        this.name_counter++;
+        return "Untitled" + suffix;
+    };
+
+    ShellTestEnvironment.prototype.on_new_harness_properties = function() {};
+
+    ShellTestEnvironment.prototype.on_tests_ready = function() {};
+
+    ShellTestEnvironment.prototype.add_on_loaded_callback = function(callback) {
+        if (this.all_loaded) {
+            callback();
+        } else {
+            this.on_loaded_callback = callback;
+        }
+    };
+
+    ShellTestEnvironment.prototype.test_timeout = function() {
+        // Tests running in a shell don't have a default timeout, so behave as
+        // if settings.explicit_timeout is true.
+        return null;
+    };
+
     function create_test_environment() {
-        if ('document' in self) {
+        if ('document' in global_scope) {
             return new WindowTestEnvironment();
         }
-        if ('DedicatedWorkerGlobalScope' in self &&
-            self instanceof DedicatedWorkerGlobalScope) {
+        if ('DedicatedWorkerGlobalScope' in global_scope &&
+            global_scope instanceof DedicatedWorkerGlobalScope) {
             return new DedicatedWorkerTestEnvironment();
         }
-        if ('SharedWorkerGlobalScope' in self &&
-            self instanceof SharedWorkerGlobalScope) {
+        if ('SharedWorkerGlobalScope' in global_scope &&
+            global_scope instanceof SharedWorkerGlobalScope) {
             return new SharedWorkerTestEnvironment();
         }
-        if ('ServiceWorkerGlobalScope' in self &&
-            self instanceof ServiceWorkerGlobalScope) {
+        if ('ServiceWorkerGlobalScope' in global_scope &&
+            global_scope instanceof ServiceWorkerGlobalScope) {
             return new ServiceWorkerTestEnvironment();
         }
-        if ('WorkerGlobalScope' in self &&
-            self instanceof WorkerGlobalScope) {
+        if ('WorkerGlobalScope' in global_scope &&
+            global_scope instanceof WorkerGlobalScope) {
             return new DedicatedWorkerTestEnvironment();
         }
 
+        if (!('location' in global_scope)) {
+            return new ShellTestEnvironment();
+        }
+
         throw new Error("Unsupported test environment");
     }
 
     var test_environment = create_test_environment();
 
     function is_shared_worker(worker) {
-        return 'SharedWorker' in self && worker instanceof SharedWorker;
+        return 'SharedWorker' in global_scope && worker instanceof SharedWorker;
     }
 
     function is_service_worker(worker) {
-        return 'ServiceWorker' in self && worker instanceof ServiceWorker;
+        // The worker object may be from another execution context,
+        // so do not use instanceof here.
+        return 'ServiceWorker' in global_scope &&
+            Object.prototype.toString.call(worker) == '[object ServiceWorker]';
     }
 
     /*
      * API functions
      */
-
     function test(func, name, properties)
     {
         var test_name = name ? name : test_environment.next_default_test_name();
         properties = properties ? properties : {};
         var test_obj = new Test(test_name, properties);
-        test_obj.step(func, test_obj, test_obj);
+        var value = test_obj.step(func, test_obj, test_obj);
+
+        if (value !== undefined) {
+            var msg = "Test named \"" + test_name +
+                "\" inappropriately returned a value";
+
+            try {
+                if (value && value.hasOwnProperty("then")) {
+                    msg += ", consider using `promise_test` instead";
+                }
+            } catch (err) {}
+
+            tests.status.status = tests.status.ERROR;
+            tests.status.message = msg;
+        }
+
         if (test_obj.phase === test_obj.phases.STARTED) {
             test_obj.done();
         }
@@ -522,31 +580,47 @@ policies and contribution forms [3].
 
     function promise_test(func, name, properties) {
         var test = async_test(name, properties);
+        test._is_promise_test = true;
+
         // If there is no promise tests queue make one.
         if (!tests.promise_tests) {
             tests.promise_tests = Promise.resolve();
         }
         tests.promise_tests = tests.promise_tests.then(function() {
-            var donePromise = new Promise(function(resolve) {
-                test.add_cleanup(resolve);
-            });
-            var promise = test.step(func, test, test);
-            test.step(function() {
-                assert_not_equals(promise, undefined);
-            });
-            Promise.resolve(promise).then(
-                    function() {
+            return new Promise(function(resolve) {
+                var promise = test.step(func, test, test);
+
+                test.step(function() {
+                    assert(!!promise, "promise_test", null,
+                           "test body must return a 'thenable' object (received ${value})",
+                           {value:promise});
+                    assert(typeof promise.then === "function", "promise_test", null,
+                           "test body must return a 'thenable' object (received an object with no `then` method)",
+                           null);
+                });
+
+                // Test authors may use the `step` method within a
+                // `promise_test` even though this reflects a mixture of
+                // asynchronous control flow paradigms. The "done" callback
+                // should be registered prior to the resolution of the
+                // user-provided Promise to avoid timeouts in cases where the
+                // Promise does not settle but a `step` function has thrown an
+                // error.
+                add_test_done_callback(test, resolve);
+
+                Promise.resolve(promise)
+                    .catch(test.step_func(
+                        function(value) {
+                            if (value instanceof AssertionError) {
+                                throw value;
+                            }
+                            assert(false, "promise_test", null,
+                                   "Unhandled rejection with value: ${value}", {value:value});
+                        }))
+                    .then(function() {
                         test.done();
-                    })
-                .catch(test.step_func(
-                    function(value) {
-                        if (value instanceof AssertionError) {
-                            throw value;
-                        }
-                        assert(false, "promise_test", null,
-                               "Unhandled rejection with value: ${value}", {value:value});
-                    }));
-            return donePromise;
+                    });
+                });
         });
     }
 
@@ -561,7 +635,7 @@ policies and contribution forms [3].
      * which can make it a lot easier to test a very specific series of events,
      * including ensuring that unexpected events are not fired at any point.
      */
-    function EventWatcher(test, watchedNode, eventTypes)
+    function EventWatcher(test, watchedNode, eventTypes, timeoutPromise)
     {
         if (typeof eventTypes == 'string') {
             eventTypes = [eventTypes];
@@ -569,12 +643,21 @@ policies and contribution forms [3].
 
         var waitingFor = null;
 
+        // This is null unless we are recording all events, in which case it
+        // will be an Array object.
+        var recordedEvents = null;
+
         var eventHandler = test.step_func(function(evt) {
             assert_true(!!waitingFor,
                         'Not expecting event, but got ' + evt.type + ' event');
             assert_equals(evt.type, waitingFor.types[0],
                           'Expected ' + waitingFor.types[0] + ' event, but got ' +
                           evt.type + ' event instead');
+
+            if (Array.isArray(recordedEvents)) {
+                recordedEvents.push(evt);
+            }
+
             if (waitingFor.types.length > 1) {
                 // Pop first event from array
                 waitingFor.types.shift();
@@ -585,7 +668,10 @@ policies and contribution forms [3].
             // need to set waitingFor.
             var resolveFunc = waitingFor.resolve;
             waitingFor = null;
-            resolveFunc(evt);
+            // Likewise, we should reset the state of recordedEvents.
+            var result = recordedEvents || evt;
+            recordedEvents = null;
+            resolveFunc(result);
         });
 
         for (var i = 0; i < eventTypes.length; i++) {
@@ -594,16 +680,59 @@ policies and contribution forms [3].
 
         /**
          * Returns a Promise that will resolve after the specified event or
-         * series of events has occured.
+         * series of events has occurred.
+         *
+         * @param options An optional options object. If the 'record' property
+         *                on this object has the value 'all', when the Promise
+         *                returned by this function is resolved,  *all* Event
+         *                objects that were waited for will be returned as an
+         *                array.
+         *
+         * For example,
+         *
+         * ```js
+         * const watcher = new EventWatcher(t, div, [ 'animationstart',
+         *                                            'animationiteration',
+         *                                            'animationend' ]);
+         * return watcher.wait_for([ 'animationstart', 'animationend' ],
+         *                         { record: 'all' }).then(evts => {
+         *   assert_equals(evts[0].elapsedTime, 0.0);
+         *   assert_equals(evts[1].elapsedTime, 2.0);
+         * });
+         * ```
          */
-        this.wait_for = function(types) {
+        this.wait_for = function(types, options) {
             if (waitingFor) {
                 return Promise.reject('Already waiting for an event or events');
             }
             if (typeof types == 'string') {
                 types = [types];
             }
+            if (options && options.record && options.record === 'all') {
+                recordedEvents = [];
+            }
             return new Promise(function(resolve, reject) {
+                var timeout = test.step_func(function() {
+                    // If the timeout fires after the events have been received
+                    // or during a subsequent call to wait_for, ignore it.
+                    if (!waitingFor || waitingFor.resolve !== resolve)
+                        return;
+
+                    // This should always fail, otherwise we should have
+                    // resolved the promise.
+                    assert_true(waitingFor.types.length == 0,
+                                'Timed out waiting for ' + waitingFor.types.join(', '));
+                    var result = recordedEvents;
+                    recordedEvents = null;
+                    var resolveFunc = waitingFor.resolve;
+                    waitingFor = null;
+                    resolveFunc(result);
+                });
+
+                if (timeoutPromise) {
+                    timeoutPromise().then(timeout);
+                }
+
                 waitingFor = {
                     types: types,
                     resolve: resolve,
@@ -618,7 +747,7 @@ policies and contribution forms [3].
             }
         };
 
-        test.add_cleanup(stop_watching);
+        test._add_cleanup(stop_watching);
 
         return this;
     }
@@ -645,6 +774,8 @@ policies and contribution forms [3].
             tests.set_file_is_test();
         }
         if (tests.file_is_test) {
+            // file is test files never have asynchronous cleanup logic,
+            // meaning the fully-synchronous `done` function can be used here.
             tests.tests[0].done();
         }
         tests.end_wait();
@@ -919,6 +1050,9 @@ policies and contribution forms [3].
 
     function assert_object_equals(actual, expected, description)
     {
+         assert(typeof actual === "object" && actual !== null, "assert_object_equals", description,
+                                                               "value is ${actual}, expected object",
+                                                               {actual: actual});
          //This needs to be improved a great deal
          function check_equal(actual, expected, stack)
          {
@@ -952,6 +1086,10 @@ policies and contribution forms [3].
 
     function assert_array_equals(actual, expected, description)
     {
+        assert(typeof actual === "object" && actual !== null && "length" in actual,
+               "assert_array_equals", description,
+               "value is ${actual}, expected array",
+               {actual:actual});
         assert(actual.length === expected.length,
                "assert_array_equals", description,
                "lengths differ, expected ${expected} got ${actual}",
@@ -971,10 +1109,38 @@ policies and contribution forms [3].
     }
     expose(assert_array_equals, "assert_array_equals");
 
+    function assert_array_approx_equals(actual, expected, epsilon, description)
+    {
+        /*
+         * Test if two primitive arrays are equal within +/- epsilon
+         */
+        assert(actual.length === expected.length,
+               "assert_array_approx_equals", description,
+               "lengths differ, expected ${expected} got ${actual}",
+               {expected:expected.length, actual:actual.length});
+
+        for (var i = 0; i < actual.length; i++) {
+            assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),
+                   "assert_array_approx_equals", description,
+                   "property ${i}, property expected to be ${expected} but was ${actual}",
+                   {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing",
+                   actual:actual.hasOwnProperty(i) ? "present" : "missing"});
+            assert(typeof actual[i] === "number",
+                   "assert_array_approx_equals", description,
+                   "property ${i}, expected a number but got a ${type_actual}",
+                   {i:i, type_actual:typeof actual[i]});
+            assert(Math.abs(actual[i] - expected[i]) <= epsilon,
+                   "assert_array_approx_equals", description,
+                   "property ${i}, expected ${expected} +/- ${epsilon}, expected ${expected} but got ${actual}",
+                   {i:i, expected:expected[i], actual:actual[i], epsilon:epsilon});
+        }
+    }
+    expose(assert_array_approx_equals, "assert_array_approx_equals");
+
     function assert_approx_equals(actual, expected, epsilon, description)
     {
         /*
-         * Test if two primitive numbers are equal withing +/- epsilon
+         * Test if two primitive numbers are equal within +/- epsilon
          */
         assert(typeof actual === "number",
                "assert_approx_equals", description,
@@ -1110,24 +1276,19 @@ policies and contribution forms [3].
     expose(assert_class_string, "assert_class_string");
 
 
-    function _assert_own_property(name) {
-        return function(object, property_name, description)
-        {
-            assert(object.hasOwnProperty(property_name),
-                   name, description,
-                   "expected property ${p} missing", {p:property_name});
-        };
+    function assert_own_property(object, property_name, description) {
+        assert(object.hasOwnProperty(property_name),
+               "assert_own_property", description,
+               "expected property ${p} missing", {p:property_name});
     }
-    expose(_assert_own_property("assert_exists"), "assert_exists");
-    expose(_assert_own_property("assert_own_property"), "assert_own_property");
+    expose(assert_own_property, "assert_own_property");
 
-    function assert_not_exists(object, property_name, description)
-    {
+    function assert_not_own_property(object, property_name, description) {
         assert(!object.hasOwnProperty(property_name),
-               "assert_not_exists", description,
-               "unexpected property ${p} found", {p:property_name});
+               "assert_not_own_property", description,
+               "unexpected property ${p} is found on object", {p:property_name});
     }
-    expose(assert_not_exists, "assert_not_exists");
+    expose(assert_not_own_property, "assert_not_own_property");
 
     function _assert_inherits(name) {
         return function (object, property_name, description)
@@ -1171,6 +1332,13 @@ policies and contribution forms [3].
     }
     expose(assert_readonly, "assert_readonly");
 
+    /**
+     * Assert an Exception with the expected code is thrown.
+     *
+     * @param {object|number|string} code The expected exception code.
+     * @param {Function} func Function which should throw.
+     * @param {string} description Error description for the case that the error is not thrown.
+     */
     function assert_throws(code, func, description)
     {
         try {
@@ -1181,11 +1349,22 @@ policies and contribution forms [3].
             if (e instanceof AssertionError) {
                 throw e;
             }
+
+            assert(typeof e === "object",
+                   "assert_throws", description,
+                   "${func} threw ${e} with type ${type}, not an object",
+                   {func:func, e:e, type:typeof e});
+
+            assert(e !== null,
+                   "assert_throws", description,
+                   "${func} threw null, not an object",
+                   {func:func});
+
             if (code === null) {
-                return;
+                throw new AssertionError('Test bug: need to pass exception to assert_throws()');
             }
             if (typeof code === "object") {
-                assert(typeof e == "object" && "name" in e && e.name == code.name,
+                assert("name" in e && e.name == code.name,
                        "assert_throws", description,
                        "${func} threw ${actual} (${actual_name}) expected ${expected} (${expected_name})",
                                     {func:func, actual:e, actual_name:e.name,
@@ -1257,15 +1436,31 @@ policies and contribution forms [3].
                 NotAllowedError: 0
             };
 
-            if (!(name in name_code_map)) {
-                throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
+            var code_name_map = {};
+            for (var key in name_code_map) {
+                if (name_code_map[key] > 0) {
+                    code_name_map[name_code_map[key]] = key;
+                }
             }
 
-            var required_props = { code: name_code_map[name] };
+            var required_props = { code: code };
+
+            if (typeof code === "number") {
+                if (code === 0) {
+                    throw new AssertionError('Test bug: ambiguous DOMException code 0 passed to assert_throws()');
+                } else if (!(code in code_name_map)) {
+                    throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
+                }
+                name = code_name_map[code];
+            } else if (typeof code === "string") {
+                if (!(name in name_code_map)) {
+                    throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
+                }
+                required_props.code = name_code_map[name];
+            }
 
             if (required_props.code === 0 ||
-               (typeof e == "object" &&
-                "name" in e &&
+               ("name" in e &&
                 e.name !== e.name.toUpperCase() &&
                 e.name !== "DOMException")) {
                 // New style exception: also test the name property.
@@ -1277,13 +1472,8 @@ policies and contribution forms [3].
             //in.  It might be an instanceof the appropriate interface on some
             //unknown other window.  TODO: Work around this somehow?
 
-            assert(typeof e == "object",
-                   "assert_throws", description,
-                   "${func} threw ${e} with type ${type}, not an object",
-                   {func:func, e:e, type:typeof e});
-
             for (var prop in required_props) {
-                assert(typeof e == "object" && prop in e && e[prop] == required_props[prop],
+                assert(prop in e && e[prop] == required_props[prop],
                        "assert_throws", description,
                        "${func} threw ${e} that is not a DOMException " + code + ": property ${prop} is equal to ${actual}, expected ${expected}",
                        {func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]});
@@ -1326,26 +1516,35 @@ policies and contribution forms [3].
         }
         this.name = name;
 
-        this.phase = this.phases.INITIAL;
+        this.phase = (tests.is_aborted || tests.phase === tests.phases.COMPLETE) ?
+            this.phases.COMPLETE : this.phases.INITIAL;
 
         this.status = this.NOTRUN;
         this.timeout_id = null;
         this.index = null;
 
         this.properties = properties;
-        var timeout = properties.timeout ? properties.timeout : settings.test_timeout;
-        if (timeout !== null) {
-            this.timeout_length = timeout * tests.timeout_multiplier;
-        } else {
-            this.timeout_length = null;
+        this.timeout_length = settings.test_timeout;
+        if (this.timeout_length !== null) {
+            this.timeout_length *= tests.timeout_multiplier;
         }
 
         this.message = null;
         this.stack = null;
 
         this.steps = [];
+        this._is_promise_test = false;
 
         this.cleanup_callbacks = [];
+        this._user_defined_cleanup_count = 0;
+        this._done_callbacks = [];
+
+        // Tests declared following harness completion are likely an indication
+        // of a programming error, but they cannot be reported
+        // deterministically.
+        if (tests.phase === tests.phases.COMPLETE) {
+            return;
+        }
 
         tests.push(this);
     }
@@ -1363,7 +1562,8 @@ policies and contribution forms [3].
         INITIAL:0,
         STARTED:1,
         HAS_RESULT:2,
-        COMPLETE:3
+        CLEANING:3,
+        COMPLETE:4
     };
 
     Test.prototype.structured_clone = function()
@@ -1374,12 +1574,14 @@ policies and contribution forms [3].
             this._structured_clone = merge({
                 name:String(this.name),
                 properties:merge({}, this.properties),
+                phases:merge({}, this.phases)
             }, Test.statuses);
         }
         this._structured_clone.status = this.status;
         this._structured_clone.message = this.message;
         this._structured_clone.stack = this.stack;
         this._structured_clone.index = this.index;
+        this._structured_clone.phase = this.phase;
         return this._structured_clone;
     };
 
@@ -1389,7 +1591,7 @@ policies and contribution forms [3].
             return;
         }
         this.phase = this.phases.STARTED;
-        //If we don't get a result before the harness times out that will be a test timout
+        //If we don't get a result before the harness times out that will be a test timeout
         this.set_status(this.TIMEOUT, "Test timed out");
 
         tests.started = true;
@@ -1468,13 +1670,25 @@ policies and contribution forms [3].
         }), timeout * tests.timeout_multiplier);
     }
 
-    Test.prototype.add_cleanup = function(callback) {
+    /*
+     * Private method for registering cleanup functions. `testharness.js`
+     * internals should use this method instead of the public `add_cleanup`
+     * method in order to hide implementation details from the harness status
+     * message in the case errors.
+     */
+    Test.prototype._add_cleanup = function(callback) {
         this.cleanup_callbacks.push(callback);
     };
 
-    Test.prototype.force_timeout = function() {
-        this.set_status(this.TIMEOUT);
-        this.phase = this.phases.HAS_RESULT;
+    /*
+     * Schedule a function to be run after the test result is known, regardless
+     * of passing or failing state. The behavior of this function will not
+     * influence the result of the test, but if an exception is thrown, the
+     * test harness will report an error.
+     */
+    Test.prototype.add_cleanup = function(callback) {
+        this._user_defined_cleanup_count += 1;
+        this._add_cleanup(callback);
     };
 
     Test.prototype.set_timeout = function()
@@ -1503,9 +1717,15 @@ policies and contribution forms [3].
         this.done();
     };
 
+    Test.prototype.force_timeout = Test.prototype.timeout;
+
+    /**
+     * Update the test status, initiate "cleanup" functions, and signal test
+     * completion.
+     */
     Test.prototype.done = function()
     {
-        if (this.phase == this.phases.COMPLETE) {
+        if (this.phase >= this.phases.CLEANING) {
             return;
         }
 
@@ -1513,20 +1733,133 @@ policies and contribution forms [3].
             this.set_status(this.PASS, null);
         }
 
-        this.phase = this.phases.COMPLETE;
+        if (global_scope.clearTimeout) {
+            clearTimeout(this.timeout_id);
+        }
 
-        clearTimeout(this.timeout_id);
-        tests.result(this);
         this.cleanup();
     };
 
+    function add_test_done_callback(test, callback)
+    {
+        if (test.phase === test.phases.COMPLETE) {
+            callback();
+            return;
+        }
+
+        test._done_callbacks.push(callback);
+    }
+
+    /*
+     * Invoke all specified cleanup functions. If one or more produce an error,
+     * the context is in an unpredictable state, so all further testing should
+     * be cancelled.
+     */
     Test.prototype.cleanup = function() {
+        var error_count = 0;
+        var bad_value_count = 0;
+        function on_error() {
+            error_count += 1;
+            // Abort tests immediately so that tests declared within subsequent
+            // cleanup functions are not run.
+            tests.abort();
+        }
+        var this_obj = this;
+        var results = [];
+
+        this.phase = this.phases.CLEANING;
+
         forEach(this.cleanup_callbacks,
                 function(cleanup_callback) {
-                    cleanup_callback();
+                    var result;
+
+                    try {
+                        result = cleanup_callback();
+                    } catch (e) {
+                        on_error();
+                        return;
+                    }
+
+                    if (!is_valid_cleanup_result(this_obj, result)) {
+                        bad_value_count += 1;
+                        // Abort tests immediately so that tests declared
+                        // within subsequent cleanup functions are not run.
+                        tests.abort();
+                    }
+
+                    results.push(result);
                 });
+
+        if (!this._is_promise_test) {
+            cleanup_done(this_obj, error_count, bad_value_count);
+        } else {
+            all_async(results,
+                      function(result, done) {
+                          if (result && typeof result.then === "function") {
+                              result
+                                  .then(null, on_error)
+                                  .then(done);
+                          } else {
+                              done();
+                          }
+                      },
+                      function() {
+                          cleanup_done(this_obj, error_count, bad_value_count);
+                      });
+        }
     };
 
+    /**
+     * Determine if the return value of a cleanup function is valid for a given
+     * test. Any test may return the value `undefined`. Tests created with
+     * `promise_test` may alternatively return "thenable" object values.
+     */
+    function is_valid_cleanup_result(test, result) {
+        if (result === undefined) {
+            return true;
+        }
+
+        if (test._is_promise_test) {
+            return result && typeof result.then === "function";
+        }
+
+        return false;
+    }
+
+    function cleanup_done(test, error_count, bad_value_count) {
+        if (error_count || bad_value_count) {
+            var total = test._user_defined_cleanup_count;
+
+            tests.status.status = tests.status.ERROR;
+            tests.status.message = "Test named '" + test.name +
+                "' specified " + total +
+                " 'cleanup' function" + (total > 1 ? "s" : "");
+
+            if (error_count) {
+                tests.status.message += ", and " + error_count + " failed";
+            }
+
+            if (bad_value_count) {
+                var type = test._is_promise_test ?
+                   "non-thenable" : "non-undefined";
+                tests.status.message += ", and " + bad_value_count +
+                    " returned a " + type + " value";
+            }
+
+            tests.status.message += ".";
+
+            tests.status.stack = null;
+        }
+
+        test.phase = test.phases.COMPLETE;
+        tests.result(test);
+        forEach(test._done_callbacks,
+                function(callback) {
+                    callback();
+                });
+        test._done_callbacks.length = 0;
+    }
+
     /*
      * A RemoteTest object mirrors a Test object on a remote worker. The
      * associated RemoteWorker updates the RemoteTest object in response to
@@ -1543,6 +1876,7 @@ policies and contribution forms [3].
         this.index = null;
         this.phase = this.phases.INITIAL;
         this.update_state_from(clone);
+        this._done_callbacks = [];
         tests.push(this);
     }
 
@@ -1550,17 +1884,40 @@ policies and contribution forms [3].
         var clone = {};
         Object.keys(this).forEach(
                 (function(key) {
-                    if (typeof(this[key]) === "object") {
-                        clone[key] = merge({}, this[key]);
+                    var value = this[key];
+                    // `RemoteTest` instances are responsible for managing
+                    // their own "done" callback functions, so those functions
+                    // are not relevant in other execution contexts. Because of
+                    // this (and because Function values cannot be serialized
+                    // for cross-realm transmittance), the property should not
+                    // be considered when cloning instances.
+                    if (key === '_done_callbacks' ) {
+                        return;
+                    }
+
+                    if (typeof value === "object" && value !== null) {
+                        clone[key] = merge({}, value);
                     } else {
-                        clone[key] = this[key];
+                        clone[key] = value;
                     }
                 }).bind(this));
         clone.phases = merge({}, this.phases);
         return clone;
     };
 
-    RemoteTest.prototype.cleanup = function() {};
+    /**
+     * `RemoteTest` instances are objects which represent tests running in
+     * another realm. They do not define "cleanup" functions (if necessary,
+     * such functions are defined on the associated `Test` instance within the
+     * external realm). However, `RemoteTests` may have "done" callbacks (e.g.
+     * as attached by the `Tests` instance responsible for tracking the overall
+     * test status in the parent realm). The `cleanup` method delegates to
+     * `done` in order to ensure that such callbacks are invoked following the
+     * completion of the `RemoteTest`.
+     */
+    RemoteTest.prototype.cleanup = function() {
+        this.done();
+    };
     RemoteTest.prototype.phases = Test.prototype.phases;
     RemoteTest.prototype.update_state_from = function(clone) {
         this.status = clone.status;
@@ -1572,76 +1929,99 @@ policies and contribution forms [3].
     };
     RemoteTest.prototype.done = function() {
         this.phase = this.phases.COMPLETE;
+
+        forEach(this._done_callbacks,
+                function(callback) {
+                    callback();
+                });
     }
 
     /*
-     * A RemoteWorker listens for test events from a worker. These events are
-     * then used to construct and maintain RemoteTest objects that mirror the
-     * tests running on the remote worker.
+     * A RemoteContext listens for test events from a remote test context, such
+     * as another window or a worker. These events are then used to construct
+     * and maintain RemoteTest objects that mirror the tests running in the
+     * remote context.
+     *
+     * An optional third parameter can be used as a predicate to filter incoming
+     * MessageEvents.
      */
-    function RemoteWorker(worker) {
+    function RemoteContext(remote, message_target, message_filter) {
         this.running = true;
+        this.started = false;
         this.tests = new Array();
+        this.early_exception = null;
 
         var this_obj = this;
-        worker.onerror = function(error) { this_obj.worker_error(error); };
-
-        var message_port;
-
-        if (is_service_worker(worker)) {
-            if (window.MessageChannel) {
-                // The ServiceWorker's implicit MessagePort is currently not
-                // reliably accessible from the ServiceWorkerGlobalScope due to
-                // Blink setting MessageEvent.source to null for messages sent
-                // via ServiceWorker.postMessage(). Until that's resolved,
-                // create an explicit MessageChannel and pass one end to the
-                // worker.
-                var message_channel = new MessageChannel();
-                message_port = message_channel.port1;
-                message_port.start();
-                worker.postMessage({type: "connect"}, [message_channel.port2]);
-            } else {
-                // If MessageChannel is not available, then try the
-                // ServiceWorker.postMessage() approach using MessageEvent.source
-                // on the other end.
-                message_port = navigator.serviceWorker;
-                worker.postMessage({type: "connect"});
+        // If remote context is cross origin assigning to onerror is not
+        // possible, so silently catch those errors.
+        try {
+          remote.onerror = function(error) { this_obj.remote_error(error); };
+        } catch (e) {
+          // Ignore.
+        }
+
+        // Keeping a reference to the remote object and the message handler until
+        // remote_done() is seen prevents the remote object and its message channel
+        // from going away before all the messages are dispatched.
+        this.remote = remote;
+        this.message_target = message_target;
+        this.message_handler = function(message) {
+            var passesFilter = !message_filter || message_filter(message);
+            // The reference to the `running` property in the following
+            // condition is unnecessary because that value is only set to
+            // `false` after the `message_handler` function has been
+            // unsubscribed.
+            // TODO: Simplify the condition by removing the reference.
+            if (this_obj.running && message.data && passesFilter &&
+                (message.data.type in this_obj.message_handlers)) {
+                this_obj.message_handlers[message.data.type].call(this_obj, message.data);
             }
-        } else if (is_shared_worker(worker)) {
-            message_port = worker.port;
-        } else {
-            message_port = worker;
-        }
+        };
 
-        // Keeping a reference to the worker until worker_done() is seen
-        // prevents the Worker object and its MessageChannel from going away
-        // before all the messages are dispatched.
-        this.worker = worker;
+        if (self.Promise) {
+            this.done = new Promise(function(resolve) {
+                this_obj.doneResolve = resolve;
+            });
+        }
 
-        message_port.onmessage =
-            function(message) {
-                if (this_obj.running && (message.data.type in this_obj.message_handlers)) {
-                    this_obj.message_handlers[message.data.type].call(this_obj, message.data);
-                }
-            };
+        this.message_target.addEventListener("message", this.message_handler);
     }
 
-    RemoteWorker.prototype.worker_error = function(error) {
+    RemoteContext.prototype.remote_error = function(error) {
+        if (error.preventDefault) {
+            error.preventDefault();
+        }
+
+        // Defer interpretation of errors until the testing protocol has
+        // started and the remote test's `allow_uncaught_exception` property
+        // is available.
+        if (!this.started) {
+            this.early_exception = error;
+        } else if (!this.allow_uncaught_exception) {
+            this.report_uncaught(error);
+        }
+    };
+
+    RemoteContext.prototype.report_uncaught = function(error) {
         var message = error.message || String(error);
         var filename = (error.filename ? " " + error.filename: "");
-        // FIXME: Display worker error states separately from main document
+        // FIXME: Display remote error states separately from main document
         // error state.
-        this.worker_done({
-            status: {
-                status: tests.status.ERROR,
-                message: "Error in worker" + filename + ": " + message,
-                stack: error.stack
-            }
-        });
-        error.preventDefault();
+        tests.set_status(tests.status.ERROR,
+                         "Error in remote" + filename + ": " + message,
+                         error.stack);
+    };
+
+    RemoteContext.prototype.start = function(data) {
+        this.started = true;
+        this.allow_uncaught_exception = data.properties.allow_uncaught_exception;
+
+        if (this.early_exception && !this.allow_uncaught_exception) {
+            this.report_uncaught(this.early_exception);
+        }
     };
 
-    RemoteWorker.prototype.test_state = function(data) {
+    RemoteContext.prototype.test_state = function(data) {
         var remote_test = this.tests[data.test.index];
         if (!remote_test) {
             remote_test = new RemoteTest(data.test);
@@ -1651,31 +2031,46 @@ policies and contribution forms [3].
         tests.notify_test_state(remote_test);
     };
 
-    RemoteWorker.prototype.test_done = function(data) {
+    RemoteContext.prototype.test_done = function(data) {
         var remote_test = this.tests[data.test.index];
         remote_test.update_state_from(data.test);
         remote_test.done();
         tests.result(remote_test);
     };
 
-    RemoteWorker.prototype.worker_done = function(data) {
+    RemoteContext.prototype.remote_done = function(data) {
         if (tests.status.status === null &&
             data.status.status !== data.status.OK) {
-            tests.status.status = data.status.status;
-            tests.status.message = data.status.message;
-            tests.status.stack = data.status.stack;
+            tests.set_status(data.status.status, data.status.message, data.status.sack);
         }
+
+        this.message_target.removeEventListener("message", this.message_handler);
         this.running = false;
-        this.worker = null;
+
+        // If remote context is cross origin assigning to onerror is not
+        // possible, so silently catch those errors.
+        try {
+          this.remote.onerror = null;
+        } catch (e) {
+          // Ignore.
+        }
+
+        this.remote = null;
+        this.message_target = null;
+        if (this.doneResolve) {
+            this.doneResolve();
+        }
+
         if (tests.all_done()) {
             tests.complete();
         }
     };
 
-    RemoteWorker.prototype.message_handlers = {
-        test_state: RemoteWorker.prototype.test_state,
-        result: RemoteWorker.prototype.test_done,
-        complete: RemoteWorker.prototype.worker_done
+    RemoteContext.prototype.message_handlers = {
+        start: RemoteContext.prototype.start,
+        test_state: RemoteContext.prototype.test_state,
+        result: RemoteContext.prototype.test_done,
+        complete: RemoteContext.prototype.remote_done
     };
 
     /*
@@ -1743,7 +2138,7 @@ policies and contribution forms [3].
         this.test_done_callbacks = [];
         this.all_done_callbacks = [];
 
-        this.pending_workers = [];
+        this.pending_remotes = [];
 
         this.status = new TestsStatus();
 
@@ -1785,6 +2180,9 @@ policies and contribution forms [3].
                     }
                 } else if (p == "timeout_multiplier") {
                     this.timeout_multiplier = value;
+                    if (this.timeout_length) {
+                         this.timeout_length *= this.timeout_multiplier;
+                    }
                 }
             }
         }
@@ -1811,20 +2209,55 @@ policies and contribution forms [3].
         async_test();
     };
 
+    Tests.prototype.set_status = function(status, message, stack)
+    {
+        this.status.status = status;
+        this.status.message = message;
+        this.status.stack = stack ? stack : null;
+    };
+
     Tests.prototype.set_timeout = function() {
-        var this_obj = this;
-        clearTimeout(this.timeout_id);
-        if (this.timeout_length !== null) {
-            this.timeout_id = setTimeout(function() {
-                                             this_obj.timeout();
-                                         }, this.timeout_length);
+        if (global_scope.clearTimeout) {
+            var this_obj = this;
+            clearTimeout(this.timeout_id);
+            if (this.timeout_length !== null) {
+                this.timeout_id = setTimeout(function() {
+                                                 this_obj.timeout();
+                                             }, this.timeout_length);
+            }
         }
     };
 
     Tests.prototype.timeout = function() {
+        var test_in_cleanup = null;
+
         if (this.status.status === null) {
-            this.status.status = this.status.TIMEOUT;
+            forEach(this.tests,
+                    function(test) {
+                        // No more than one test is expected to be in the
+                        // "CLEANUP" phase at any time
+                        if (test.phase === test.phases.CLEANING) {
+                            test_in_cleanup = test;
+                        }
+
+                        test.phase = test.phases.COMPLETE;
+                    });
+
+            // Timeouts that occur while a test is in the "cleanup" phase
+            // indicate that some global state was not properly reverted. This
+            // invalidates the overall test execution, so the timeout should be
+            // reported as an error and cancel the execution of any remaining
+            // tests.
+            if (test_in_cleanup) {
+                this.status.status = this.status.ERROR;
+                this.status.message = "Timeout while running cleanup for " +
+                    "test named \"" + test_in_cleanup.name + "\".";
+                tests.status.stack = null;
+            } else {
+                this.status.status = this.status.TIMEOUT;
+            }
         }
+
         this.complete();
     };
 
@@ -1855,10 +2288,10 @@ policies and contribution forms [3].
     };
 
     Tests.prototype.all_done = function() {
-        return (this.tests.length > 0 && test_environment.all_loaded &&
-                this.num_pending === 0 && !this.wait_for_finish &&
+        return this.tests.length > 0 && test_environment.all_loaded &&
+                (this.num_pending === 0 || this.is_aborted) && !this.wait_for_finish &&
                 !this.processing_callbacks &&
-                !this.pending_workers.some(function(w) { return w.running; }));
+                !this.pending_remotes.some(function(w) { return w.running; });
     };
 
     Tests.prototype.start = function() {
@@ -1877,10 +2310,11 @@ policies and contribution forms [3].
 
     Tests.prototype.result = function(test)
     {
-        if (this.phase > this.phases.HAVE_RESULTS) {
-            return;
+        // If the harness has already transitioned beyond the `HAVE_RESULTS`
+        // phase, subsequent tests should not cause it to revert.
+        if (this.phase <= this.phases.HAVE_RESULTS) {
+            this.phase = this.phases.HAVE_RESULTS;
         }
-        this.phase = this.phases.HAVE_RESULTS;
         this.num_pending--;
         this.notify_result(test);
     };
@@ -1903,25 +2337,137 @@ policies and contribution forms [3].
         if (this.phase === this.phases.COMPLETE) {
             return;
         }
-        this.phase = this.phases.COMPLETE;
         var this_obj = this;
-        this.tests.forEach(
-            function(x)
-            {
-                if (x.phase < x.phases.COMPLETE) {
-                    this_obj.notify_result(x);
-                    x.cleanup();
-                    x.phase = x.phases.COMPLETE;
-                }
-            }
-        );
-        this.notify_complete();
+        var all_complete = function() {
+            this_obj.phase = this_obj.phases.COMPLETE;
+            this_obj.notify_complete();
+        };
+        var incomplete = filter(this.tests,
+                                function(test) {
+                                    return test.phase < test.phases.COMPLETE;
+                                });
+
+        /**
+         * To preserve legacy behavior, overall test completion must be
+         * signaled synchronously.
+         */
+        if (incomplete.length === 0) {
+            all_complete();
+            return;
+        }
+
+        all_async(incomplete,
+                  function(test, testDone)
+                  {
+                      if (test.phase === test.phases.INITIAL) {
+                          test.phase = test.phases.COMPLETE;
+                          testDone();
+                      } else {
+                          add_test_done_callback(test, testDone);
+                          test.cleanup();
+                      }
+                  },
+                  all_complete);
     };
 
+    /**
+     * Update the harness status to reflect an unrecoverable harness error that
+     * should cancel all further testing. Update all previously-defined tests
+     * which have not yet started to indicate that they will not be executed.
+     */
+    Tests.prototype.abort = function() {
+        this.status.status = this.status.ERROR;
+        this.is_aborted = true;
+
+        forEach(this.tests,
+                function(test) {
+                    if (test.phase === test.phases.INITIAL) {
+                        test.phase = test.phases.COMPLETE;
+                    }
+                });
+    };
+
+    /*
+     * Determine if any tests share the same `name` property. Return an array
+     * containing the names of any such duplicates.
+     */
+    Tests.prototype.find_duplicates = function() {
+        var names = Object.create(null);
+        var duplicates = [];
+
+        forEach (this.tests,
+                 function(test)
+                 {
+                     if (test.name in names && duplicates.indexOf(test.name) === -1) {
+                        duplicates.push(test.name);
+                     }
+                     names[test.name] = true;
+                 });
+
+        return duplicates;
+    };
+
+    function code_unit_str(char) {
+        return 'U+' + char.charCodeAt(0).toString(16);
+    }
+
+    function sanitize_unpaired_surrogates(str) {
+        return str.replace(/([\ud800-\udbff])(?![\udc00-\udfff])/g,
+                           function(_, unpaired)
+                           {
+                               return code_unit_str(unpaired);
+                           })
+                  // This replacement is intentionally implemented without an
+                  // ES2018 negative lookbehind assertion to support runtimes
+                  // which do not yet implement that language feature.
+                  .replace(/(^|[^\ud800-\udbff])([\udc00-\udfff])/g,
+                           function(_, previous, unpaired) {
+                              if (/[\udc00-\udfff]/.test(previous)) {
+                                  previous = code_unit_str(previous);
+                              }
+
+                              return previous + code_unit_str(unpaired);
+                           });
+    }
+
+    function sanitize_all_unpaired_surrogates(tests) {
+        forEach (tests,
+                 function (test)
+                 {
+                     var sanitized = sanitize_unpaired_surrogates(test.name);
+
+                     if (test.name !== sanitized) {
+                         test.name = sanitized;
+                         delete test._structured_clone;
+                     }
+                 });
+    }
+
     Tests.prototype.notify_complete = function() {
         var this_obj = this;
+        var duplicates;
+
         if (this.status.status === null) {
-            this.status.status = this.status.OK;
+            duplicates = this.find_duplicates();
+
+            // Some transports adhere to UTF-8's restriction on unpaired
+            // surrogates. Sanitize the titles so that the results can be
+            // consistently sent via all transports.
+            sanitize_all_unpaired_surrogates(this.tests);
+
+            // Test names are presumed to be unique within test files--this
+            // allows consumers to use them for identification purposes.
+            // Duplicated names violate this expectation and should therefore
+            // be reported as an error.
+            if (duplicates.length) {
+                this.status.status = this.status.ERROR;
+                this.status.message =
+                   duplicates.length + ' duplicate test name' +
+                   (duplicates.length > 1 ? 's' : '') + ': "' +
+                   duplicates.join('", "') + '"';
+            } else {
+                this.status.status = this.status.OK;
+            }
         }
 
         forEach (this.all_done_callbacks,
@@ -1931,19 +2477,83 @@ policies and contribution forms [3].
                  });
     };
 
+    /*
+     * Constructs a RemoteContext that tracks tests from a specific worker.
+     */
+    Tests.prototype.create_remote_worker = function(worker) {
+        var message_port;
+
+        if (is_service_worker(worker)) {
+            if (window.MessageChannel) {
+                // The ServiceWorker's implicit MessagePort is currently not
+                // reliably accessible from the ServiceWorkerGlobalScope due to
+                // Blink setting MessageEvent.source to null for messages sent
+                // via ServiceWorker.postMessage(). Until that's resolved,
+                // create an explicit MessageChannel and pass one end to the
+                // worker.
+                var message_channel = new MessageChannel();
+                message_port = message_channel.port1;
+                message_port.start();
+                worker.postMessage({type: "connect"}, [message_channel.port2]);
+            } else {
+                // If MessageChannel is not available, then try the
+                // ServiceWorker.postMessage() approach using MessageEvent.source
+                // on the other end.
+                message_port = navigator.serviceWorker;
+                worker.postMessage({type: "connect"});
+            }
+        } else if (is_shared_worker(worker)) {
+            message_port = worker.port;
+            message_port.start();
+        } else {
+            message_port = worker;
+        }
+
+        return new RemoteContext(worker, message_port);
+    };
+
+    /*
+     * Constructs a RemoteContext that tracks tests from a specific window.
+     */
+    Tests.prototype.create_remote_window = function(remote) {
+        remote.postMessage({type: "getmessages"}, "*");
+        return new RemoteContext(
+            remote,
+            window,
+            function(msg) {
+                return msg.source === remote;
+            }
+        );
+    };
+
     Tests.prototype.fetch_tests_from_worker = function(worker) {
         if (this.phase >= this.phases.COMPLETE) {
             return;
         }
 
-        this.pending_workers.push(new RemoteWorker(worker));
+        var remoteContext = this.create_remote_worker(worker);
+        this.pending_remotes.push(remoteContext);
+        return remoteContext.done;
     };
 
     function fetch_tests_from_worker(port) {
-        tests.fetch_tests_from_worker(port);
+        return tests.fetch_tests_from_worker(port);
     }
     expose(fetch_tests_from_worker, 'fetch_tests_from_worker');
 
+    Tests.prototype.fetch_tests_from_window = function(remote) {
+        if (this.phase >= this.phases.COMPLETE) {
+            return;
+        }
+
+        this.pending_remotes.push(this.create_remote_window(remote));
+    };
+
+    function fetch_tests_from_window(window) {
+        tests.fetch_tests_from_window(window);
+    }
+    expose(fetch_tests_from_window, 'fetch_tests_from_window');
+
     function timeout() {
         if (tests.timeout_length === null) {
             tests.timeout();
@@ -2036,6 +2646,9 @@ policies and contribution forms [3].
 
     Output.prototype.resolve_log = function() {
         var output_document;
+        if (this.output_node) {
+            return;
+        }
         if (typeof this.output_document === "function") {
             output_document = this.output_document.apply(undefined);
         } else {
@@ -2046,12 +2659,34 @@ policies and contribution forms [3].
         }
         var node = output_document.getElementById("log");
         if (!node) {
-            if (!document.body || document.readyState == "loading") {
+            if (output_document.readyState === "loading") {
                 return;
             }
-            node = output_document.createElement("div");
+            node = output_document.createElementNS("http://www.w3.org/1999/xhtml", "div");
             node.id = "log";
-            output_document.body.appendChild(node);
+            if (output_document.body) {
+                output_document.body.appendChild(node);
+            } else {
+                var root = output_document.documentElement;
+                var is_html = (root &&
+                               root.namespaceURI == "http://www.w3.org/1999/xhtml" &&
+                               root.localName == "html");
+                var is_svg = (output_document.defaultView &&
+                              "SVGSVGElement" in output_document.defaultView &&
+                              root instanceof output_document.defaultView.SVGSVGElement);
+                if (is_svg) {
+                    var foreignObject = output_document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
+                    foreignObject.setAttribute("width", "100%");
+                    foreignObject.setAttribute("height", "100%");
+                    root.appendChild(foreignObject);
+                    foreignObject.appendChild(node);
+                } else if (is_html) {
+                    root.appendChild(output_document.createElementNS("http://www.w3.org/1999/xhtml", "body"))
+                        .appendChild(node);
+                } else {
+                    root.appendChild(node);
+                }
+            }
         }
         this.output_document = output_document;
         this.output_node = node;
@@ -2061,11 +2696,11 @@ policies and contribution forms [3].
         if (this.phase < this.STARTED) {
             this.init();
         }
-        if (!this.enabled) {
+        if (!this.enabled || this.phase === this.COMPLETE) {
             return;
         }
+        this.resolve_log();
         if (this.phase < this.HAVE_RESULTS) {
-            this.resolve_log();
             this.phase = this.HAVE_RESULTS;
         }
         var done_count = tests.tests.length - tests.num_pending;
@@ -2102,15 +2737,11 @@ policies and contribution forms [3].
             log.removeChild(log.lastChild);
         }
 
-        var harness_url = get_harness_url();
-        if (harness_url !== null) {
-            var stylesheet = output_document.createElementNS(xhtml_ns, "link");
-            stylesheet.setAttribute("rel", "stylesheet");
-            stylesheet.setAttribute("href", harness_url + "testharness.css");
-            var heads = output_document.getElementsByTagName("head");
-            if (heads.length) {
-                heads[0].appendChild(stylesheet);
-            }
+        var stylesheet = output_document.createElementNS(xhtml_ns, "style");
+        stylesheet.textContent = stylesheetContent;
+        var heads = output_document.getElementsByTagName("head");
+        if (heads.length) {
+            heads[0].appendChild(stylesheet);
         }
 
         var status_text_harness = {};
@@ -2276,7 +2907,7 @@ policies and contribution forms [3].
     /*
      * Template code
      *
-     * A template is just a javascript structure. An element is represented as:
+     * A template is just a JavaScript structure. An element is represented as:
      *
      * [tag_name, {attr_name:attr_value}, child1, child2]
      *
@@ -2438,7 +3069,7 @@ policies and contribution forms [3].
     }
 
     /*
-     * Utility funcions
+     * Utility functions
      */
     function assert(expected_true, function_name, description, error, substitutions)
     {
@@ -2457,6 +3088,7 @@ policies and contribution forms [3].
         this.message = message;
         this.stack = this.get_stack();
     }
+    expose(AssertionError, "AssertionError");
 
     AssertionError.prototype = Object.create(Error.prototype);
 
@@ -2559,6 +3191,57 @@ policies and contribution forms [3].
         }
     }
 
+    /**
+     * Immediately invoke a "iteratee" function with a series of values in
+     * parallel and invoke a final "done" function when all of the "iteratee"
+     * invocations have signaled completion.
+     *
+     * If all callbacks complete synchronously (or if no callbacks are
+     * specified), the `done_callback` will be invoked synchronously. It is the
+     * responsibility of the caller to ensure asynchronicity in cases where
+     * that is desired.
+     *
+     * @param {array} value Zero or more values to use in the invocation of
+     *                      `iter_callback`
+     * @param {function} iter_callback A function that will be invoked once for
+     *                                 each of the provided `values`. Two
+     *                                 arguments will be available in each
+     *                                 invocation: the value from `values` and
+     *                                 a function that must be invoked to
+     *                                 signal completion
+     * @param {function} done_callback A function that will be invoked after
+     *                                 all operations initiated by the
+     *                                 `iter_callback` function have signaled
+     *                                 completion
+     */
+    function all_async(values, iter_callback, done_callback)
+    {
+        var remaining = values.length;
+
+        if (remaining === 0) {
+            done_callback();
+        }
+
+        forEach(values,
+                function(element) {
+                    var invoked = false;
+                    var elDone = function() {
+                        if (invoked) {
+                            return;
+                        }
+
+                        invoked = true;
+                        remaining -= 1;
+
+                        if (remaining === 0) {
+                            done_callback();
+                        }
+                    };
+
+                    iter_callback(element, elDone);
+                });
+    }
+
     function merge(a,b)
     {
         var rv = {};
@@ -2575,7 +3258,7 @@ policies and contribution forms [3].
     function expose(object, name)
     {
         var components = name.split(".");
-        var target = test_environment.global_scope();
+        var target = global_scope;
         for (var i = 0; i < components.length - 1; i++) {
             if (!(components[i] in target)) {
                 target[components[i]] = {};
@@ -2597,7 +3280,7 @@ policies and contribution forms [3].
     /** Returns the 'src' URL of the first <script> tag in the page to include the file 'testharness.js'. */
     function get_script_url()
     {
-        if (!('document' in self)) {
+        if (!('document' in global_scope)) {
             return undefined;
         }
 
@@ -2619,14 +3302,23 @@ policies and contribution forms [3].
         return undefined;
     }
 
-    /** Returns the URL path at which the files for testharness.js are assumed to reside (e.g., '/resources/').
-        The path is derived from inspecting the 'src' of the <script> tag that included 'testharness.js'. */
-    function get_harness_url()
+    /** Returns the <title> or filename or "Untitled" */
+    function get_title()
     {
-        var script_url = get_script_url();
-
-        // Exclude the 'testharness.js' file from the returned path, but '+ 1' to include the trailing slash.
-        return script_url ? script_url.slice(0, script_url.lastIndexOf('/') + 1) : undefined;
+        if ('document' in global_scope) {
+            //Don't use document.title to work around an Opera bug in XHTML documents
+            var title = document.getElementsByTagName("title")[0];
+            if (title && title.firstChild && title.firstChild.data) {
+                return title.firstChild.data;
+            }
+        }
+        if ('META_TITLE' in global_scope && META_TITLE) {
+            return META_TITLE;
+        }
+        if ('location' in global_scope) {
+            return location.pathname.substring(location.pathname.lastIndexOf('/') + 1, location.pathname.indexOf('.'));
+        }
+        return "Untitled";
     }
 
     function supports_post_message(w)
@@ -2640,7 +3332,7 @@ policies and contribution forms [3].
         // Touching the postMessage prop on a window can throw if the window is
         // not from the same origin AND post message is not supported in that
         // browser. So just doing an existence test here won't do, you also need
-        // to wrap it in a try..cacth block.
+        // to wrap it in a try..catch block.
         try {
             type = typeof w.postMessage;
             if (type === "function") {
@@ -2672,27 +3364,150 @@ policies and contribution forms [3].
 
     var tests = new Tests();
 
-    var error_handler = function(e) {
-        if (tests.file_is_test) {
-            var test = tests.tests[0];
-            if (test.phase >= test.phases.HAS_RESULT) {
-                return;
+    if (global_scope.addEventListener) {
+        var error_handler = function(e) {
+            if (tests.tests.length === 0 && !tests.allow_uncaught_exception) {
+                tests.set_file_is_test();
+            }
+
+            var stack;
+            if (e.error && e.error.stack) {
+                stack = e.error.stack;
+            } else {
+                stack = e.filename + ":" + e.lineno + ":" + e.colno;
+            }
+
+            if (tests.file_is_test) {
+                var test = tests.tests[0];
+                if (test.phase >= test.phases.HAS_RESULT) {
+                    return;
+                }
+                test.set_status(test.FAIL, e.message, stack);
+                test.phase = test.phases.HAS_RESULT;
+                // The following function invocation is superfluous.
+                // TODO: Remove.
+                test.done();
+            } else if (!tests.allow_uncaught_exception) {
+                tests.status.status = tests.status.ERROR;
+                tests.status.message = e.message;
+                tests.status.stack = stack;
             }
-            test.set_status(test.FAIL, e.message, e.stack);
-            test.phase = test.phases.HAS_RESULT;
-            test.done();
             done();
-        } else if (!tests.allow_uncaught_exception) {
-            tests.status.status = tests.status.ERROR;
-            tests.status.message = e.message;
-            tests.status.stack = e.stack;
-        }
-    };
+        };
 
-    addEventListener("error", error_handler, false);
-    addEventListener("unhandledrejection", function(e){ error_handler(e.reason); }, false);
+        addEventListener("error", error_handler, false);
+        addEventListener("unhandledrejection", function(e){ error_handler(e.reason); }, false);
+    }
 
     test_environment.on_tests_ready();
 
-})();
+    /**
+     * Stylesheet
+     */
+     var stylesheetContent = "\
+html {\
+    font-family:DejaVu Sans, Bitstream Vera Sans, Arial, Sans;\
+}\
+\
+#log .warning,\
+#log .warning a {\
+  color: black;\
+  background: yellow;\
+}\
+\
+#log .error,\
+#log .error a {\
+  color: white;\
+  background: red;\
+}\
+\
+section#summary {\
+    margin-bottom:1em;\
+}\
+\
+table#results {\
+    border-collapse:collapse;\
+    table-layout:fixed;\
+    width:100%;\
+}\
+\
+table#results th:first-child,\
+table#results td:first-child {\
+    width:4em;\
+}\
+\
+table#results th:last-child,\
+table#results td:last-child {\
+    width:50%;\
+}\
+\
+table#results.assertions th:last-child,\
+table#results.assertions td:last-child {\
+    width:35%;\
+}\
+\
+table#results th {\
+    padding:0;\
+    padding-bottom:0.5em;\
+    border-bottom:medium solid black;\
+}\
+\
+table#results td {\
+    padding:1em;\
+    padding-bottom:0.5em;\
+    border-bottom:thin solid black;\
+}\
+\
+tr.pass > td:first-child {\
+    color:green;\
+}\
+\
+tr.fail > td:first-child {\
+    color:red;\
+}\
+\
+tr.timeout > td:first-child {\
+    color:red;\
+}\
+\
+tr.notrun > td:first-child {\
+    color:blue;\
+}\
+\
+.pass > td:first-child, .fail > td:first-child, .timeout > td:first-child, .notrun > td:first-child {\
+    font-variant:small-caps;\
+}\
+\
+table#results span {\
+    display:block;\
+}\
+\
+table#results span.expected {\
+    font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace;\
+    white-space:pre;\
+}\
+\
+table#results span.actual {\
+    font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace;\
+    white-space:pre;\
+}\
+\
+span.ok {\
+    color:green;\
+}\
+\
+tr.error {\
+    color:red;\
+}\
+\
+span.timeout {\
+    color:red;\
+}\
+\
+span.ok, span.timeout, span.error {\
+    font-variant:small-caps;\
+}\
+";
+
+})(this);
 // vim: set expandtab shiftwidth=4 tabstop=4:
index e34d312..5e79797 100644 (file)
@@ -19,32 +19,7 @@ PASS Enqueuing a chunk, getting a reader and calling read should result in a pro
 PASS Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk 
 PASS Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk 
 PASS By default initial value of desiredSize should be 0 
-PASS Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined 
-PASS Test that pull is not called when a new ReadableStream is created with default strategy parameters and a ReadableByteStreamController 
-PASS Test that pull is called once when a new ReadableStream is created with a highWaterMark of 1 and a ReadableByteStreamController 
-PASS For a ReadableStream created with a highWaterMark of 1 and a ReadableByteStreamController, calling cancel after pull has thrown an error should result in a promise rejected with the same error 
-PASS Calling cancel after creating a ReadableStream with an underlyingByteStream's start function returning a rejected promise should result in a promise rejected with the same error 
-PASS Creating a ReadableStream with an underlyingSource with type property set to 'bytes' should succeed 
-PASS Calling error() with a this object different from ReadableByteStreamController should throw a TypeError 
-PASS Calling close() with a this object different from ReadableByteStreamController should throw a TypeError 
-PASS Calling enqueue() with a this object different from ReadableByteStreamController should throw a TypeError 
-PASS Calling enqueue() when close has been requested but not yet performed should throw a TypeError 
-PASS Calling enqueue() when stream is not readable should throw a TypeError 
-PASS Calling enqueue() with a chunk that is not an object should trhow a TypeError 
-PASS Calling enqueue() with a chunk that is not an ArrayBufferView should throw a TypeError 
-PASS Calling error() after calling close() should throw a TypeError 
-PASS Calling error() after calling error() should throw a TypeError 
-PASS Calling close() after calling close() should throw a TypeError 
-PASS Calling close() after calling error() should throw a TypeError 
-PASS Calling read() on a reader associated to a controller that has been errored should fail with provided error 
-PASS Calling read() on a reader associated to a controller that has been closed should not be rejected 
-PASS Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is undefined) 
-FAIL Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is specified) Can't find private variable: PrivateSymbol.Uint8Array
-PASS Enqueuing a chunk, getting a reader and calling read should result in a promise resolved with said chunk 
-PASS Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk 
-PASS Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk 
-PASS By default initial value of desiredSize should be 0 
-PASS Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined 
+FAIL Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined assert_object_equals: value is undefined, expected object
 PASS Test that pull is not called when a new ReadableStream is created with default strategy parameters and a ReadableByteStreamController 
 PASS Test that pull is called once when a new ReadableStream is created with a highWaterMark of 1 and a ReadableByteStreamController 
 PASS For a ReadableStream created with a highWaterMark of 1 and a ReadableByteStreamController, calling cancel after pull has thrown an error should result in a promise rejected with the same error 
diff --git a/LayoutTests/streams/readable-byte-stream-controller-worker-expected.txt b/LayoutTests/streams/readable-byte-stream-controller-worker-expected.txt
new file mode 100644 (file)
index 0000000..5e79797
--- /dev/null
@@ -0,0 +1,27 @@
+
+PASS Creating a ReadableStream with an underlyingSource with type property set to 'bytes' should succeed 
+PASS Calling error() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling close() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling enqueue() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling enqueue() when close has been requested but not yet performed should throw a TypeError 
+PASS Calling enqueue() when stream is not readable should throw a TypeError 
+PASS Calling enqueue() with a chunk that is not an object should trhow a TypeError 
+PASS Calling enqueue() with a chunk that is not an ArrayBufferView should throw a TypeError 
+PASS Calling error() after calling close() should throw a TypeError 
+PASS Calling error() after calling error() should throw a TypeError 
+PASS Calling close() after calling close() should throw a TypeError 
+PASS Calling close() after calling error() should throw a TypeError 
+PASS Calling read() on a reader associated to a controller that has been errored should fail with provided error 
+PASS Calling read() on a reader associated to a controller that has been closed should not be rejected 
+PASS Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is undefined) 
+FAIL Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is specified) Can't find private variable: PrivateSymbol.Uint8Array
+PASS Enqueuing a chunk, getting a reader and calling read should result in a promise resolved with said chunk 
+PASS Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk 
+PASS Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk 
+PASS By default initial value of desiredSize should be 0 
+FAIL Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined assert_object_equals: value is undefined, expected object
+PASS Test that pull is not called when a new ReadableStream is created with default strategy parameters and a ReadableByteStreamController 
+PASS Test that pull is called once when a new ReadableStream is created with a highWaterMark of 1 and a ReadableByteStreamController 
+PASS For a ReadableStream created with a highWaterMark of 1 and a ReadableByteStreamController, calling cancel after pull has thrown an error should result in a promise rejected with the same error 
+PASS Calling cancel after creating a ReadableStream with an underlyingByteStream's start function returning a rejected promise should result in a promise rejected with the same error 
+
diff --git a/LayoutTests/streams/readable-byte-stream-controller-worker.html b/LayoutTests/streams/readable-byte-stream-controller-worker.html
new file mode 100644 (file)
index 0000000..40b89f9
--- /dev/null
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('readable-byte-stream-controller.js'));
+</script>
index e7a8182..a5c732a 100644 (file)
@@ -2,7 +2,3 @@
 <script src='../resources/testharness.js'></script>
 <script src='../resources/testharnessreport.js'></script>
 <script src='readable-byte-stream-controller.js'></script>
-<script>
-'use strict';
-fetch_tests_from_worker(new Worker('readable-byte-stream-controller.js'));
-</script>
index 5120f9a..a069419 100644 (file)
@@ -4,23 +4,9 @@ PASS Calling getReader() with a this object different from ReadableStream should
 PASS Calling getReader({ mode: 'byob' }) with a ReadableStream whose controller is a ReadableStreamDefaultController should throw a TypeError 
 PASS Calling ReadableStreamBYOBReader.cancel() with a this object different from ReadableStreamBYOBReader should be rejected 
 PASS Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been errored should result in a promise rejected with the same error 
-PASS Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been closed should result in a promise resolved with undefined 
-PASS If controller is closed after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise resolved with undefined 
-PASS If controller has already been closed when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise resolved with undefined 
-PASS If controller is errored after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise rejected with the same error 
-PASS If controller has already been errored when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise rejected with the same error 
-PASS Calling ReadableStreamBYOBReader.releaseLock() with a this object different from ReadableStreamBYOBReader should be rejected 
-PASS Calling ReadableStreamBYOBReader.releaseLock() on a stream that is readable should result in ReadableStreamBYOBReader.closed promise to be rejected with a TypeError 
-PASS Calling ReadableStreamBYOBReader.releaseLock() on a stream that is not readable should result in ReadableStreamBYOBReader.closed promise to be rejected with a TypeError 
-PASS Calling ReadableStreamBYOBReader.read() with a this object different from ReadableStreamBYOBReader should be rejected 
-PASS Getting a ReadableStreamBYOBReader should succeed 
-PASS Calling getReader() with a this object different from ReadableStream should throw a TypeError 
-PASS Calling getReader({ mode: 'byob' }) with a ReadableStream whose controller is a ReadableStreamDefaultController should throw a TypeError 
-PASS Calling ReadableStreamBYOBReader.cancel() with a this object different from ReadableStreamBYOBReader should be rejected 
-PASS Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been errored should result in a promise rejected with the same error 
-PASS Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been closed should result in a promise resolved with undefined 
-PASS If controller is closed after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise resolved with undefined 
-PASS If controller has already been closed when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise resolved with undefined 
+FAIL Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been closed should result in a promise resolved with undefined assert_object_equals: value is undefined, expected object
+FAIL If controller is closed after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise resolved with undefined assert_object_equals: value is undefined, expected object
+FAIL If controller has already been closed when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise resolved with undefined assert_object_equals: value is undefined, expected object
 PASS If controller is errored after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise rejected with the same error 
 PASS If controller has already been errored when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise rejected with the same error 
 PASS Calling ReadableStreamBYOBReader.releaseLock() with a this object different from ReadableStreamBYOBReader should be rejected 
diff --git a/LayoutTests/streams/readable-stream-byob-reader-worker-expected.txt b/LayoutTests/streams/readable-stream-byob-reader-worker-expected.txt
new file mode 100644 (file)
index 0000000..a069419
--- /dev/null
@@ -0,0 +1,16 @@
+
+PASS Getting a ReadableStreamBYOBReader should succeed 
+PASS Calling getReader() with a this object different from ReadableStream should throw a TypeError 
+PASS Calling getReader({ mode: 'byob' }) with a ReadableStream whose controller is a ReadableStreamDefaultController should throw a TypeError 
+PASS Calling ReadableStreamBYOBReader.cancel() with a this object different from ReadableStreamBYOBReader should be rejected 
+PASS Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been errored should result in a promise rejected with the same error 
+FAIL Calling ReadableStreamBYOBReader.cancel() on a ReadableStream that has been closed should result in a promise resolved with undefined assert_object_equals: value is undefined, expected object
+FAIL If controller is closed after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise resolved with undefined assert_object_equals: value is undefined, expected object
+FAIL If controller has already been closed when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise resolved with undefined assert_object_equals: value is undefined, expected object
+PASS If controller is errored after ReadableStreamBYOBReader creation, ReadableStreamBYOBReader.closed should be a promise rejected with the same error 
+PASS If controller has already been errored when ReadableStreamBYOBReader is created, ReadableStreamBYOBReader.closed should be a promise rejected with the same error 
+PASS Calling ReadableStreamBYOBReader.releaseLock() with a this object different from ReadableStreamBYOBReader should be rejected 
+PASS Calling ReadableStreamBYOBReader.releaseLock() on a stream that is readable should result in ReadableStreamBYOBReader.closed promise to be rejected with a TypeError 
+PASS Calling ReadableStreamBYOBReader.releaseLock() on a stream that is not readable should result in ReadableStreamBYOBReader.closed promise to be rejected with a TypeError 
+PASS Calling ReadableStreamBYOBReader.read() with a this object different from ReadableStreamBYOBReader should be rejected 
+
diff --git a/LayoutTests/streams/readable-stream-byob-reader-worker.html b/LayoutTests/streams/readable-stream-byob-reader-worker.html
new file mode 100644 (file)
index 0000000..67fac03
--- /dev/null
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('readable-stream-byob-reader.js'));
+</script>
index ecce533..449b682 100644 (file)
@@ -2,7 +2,3 @@
 <script src='../resources/testharness.js'></script>
 <script src='../resources/testharnessreport.js'></script>
 <script src='readable-stream-byob-reader.js'></script>
-<script>
-'use strict';
-fetch_tests_from_worker(new Worker('readable-stream-byob-reader.js'));
-</script>
index 939e37c..c2a222a 100644 (file)
@@ -15,20 +15,4 @@ PASS Calling respondWithNewView() with an argument that is not an ArrayBufferVie
 PASS When using autoAllocateChunkSize, calling respondWithNewView() should succeed if view.byteLength is equal to autoAllocateChunkSize 
 PASS When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteOffset is different from 0 
 PASS When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteLength is different from autoAllocateChunkSize 
-PASS By default, byobRequest should be undefined 
-PASS byobRequest.view length should be equal to autoAllocateChunkSize 
-PASS Calling respond() with a this object different from ReadableStreamBYOBRequest should throw a TypeError 
-PASS Calling respond() with a negative bytesWritten value should throw a RangeError 
-PASS Calling respond() with a bytesWritten value which is not a number should throw a RangeError 
-PASS Calling respond() with a positive infinity bytesWritten value should throw a RangeError 
-PASS Calling respond() with a bytesWritten value different from 0 when stream is closed should throw a TypeError 
-PASS Calling respond() with a bytesWritten value of 0 when stream is closed should succeed 
-PASS Calling respond() with a bytesWritten value greater than autoAllocateChunkSize should fail 
-PASS Calling respond() with a bytesWritten value lower than autoAllocateChunkSize should succeed 
-PASS Calling respondWithNewView() with a this object different from ReadableStreamBYOBRequest should throw a TypeError 
-PASS Calling respondWithNewView() with an argument that is not an object should throw a TypeError 
-PASS Calling respondWithNewView() with an argument that is not an ArrayBufferView should throw a TypeError 
-PASS When using autoAllocateChunkSize, calling respondWithNewView() should succeed if view.byteLength is equal to autoAllocateChunkSize 
-PASS When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteOffset is different from 0 
-PASS When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteLength is different from autoAllocateChunkSize 
 
diff --git a/LayoutTests/streams/readable-stream-byob-request-worker-expected.txt b/LayoutTests/streams/readable-stream-byob-request-worker-expected.txt
new file mode 100644 (file)
index 0000000..c2a222a
--- /dev/null
@@ -0,0 +1,18 @@
+
+PASS By default, byobRequest should be undefined 
+PASS byobRequest.view length should be equal to autoAllocateChunkSize 
+PASS Calling respond() with a this object different from ReadableStreamBYOBRequest should throw a TypeError 
+PASS Calling respond() with a negative bytesWritten value should throw a RangeError 
+PASS Calling respond() with a bytesWritten value which is not a number should throw a RangeError 
+PASS Calling respond() with a positive infinity bytesWritten value should throw a RangeError 
+PASS Calling respond() with a bytesWritten value different from 0 when stream is closed should throw a TypeError 
+PASS Calling respond() with a bytesWritten value of 0 when stream is closed should succeed 
+PASS Calling respond() with a bytesWritten value greater than autoAllocateChunkSize should fail 
+PASS Calling respond() with a bytesWritten value lower than autoAllocateChunkSize should succeed 
+PASS Calling respondWithNewView() with a this object different from ReadableStreamBYOBRequest should throw a TypeError 
+PASS Calling respondWithNewView() with an argument that is not an object should throw a TypeError 
+PASS Calling respondWithNewView() with an argument that is not an ArrayBufferView should throw a TypeError 
+PASS When using autoAllocateChunkSize, calling respondWithNewView() should succeed if view.byteLength is equal to autoAllocateChunkSize 
+PASS When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteOffset is different from 0 
+PASS When using autoAllocateChunkSize, calling respondWithNewView() should throw a RangeError if view.byteLength is different from autoAllocateChunkSize 
+
diff --git a/LayoutTests/streams/readable-stream-byob-request-worker.html b/LayoutTests/streams/readable-stream-byob-request-worker.html
new file mode 100644 (file)
index 0000000..3f884ee
--- /dev/null
@@ -0,0 +1,8 @@
+
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('readable-stream-byob-request.js'));
+</script>
index 0394349..8d3abee 100644 (file)
@@ -3,7 +3,3 @@
 <script src='../resources/testharness.js'></script>
 <script src='../resources/testharnessreport.js'></script>
 <script src='readable-stream-byob-request.js'></script>
-<script>
-'use strict';
-fetch_tests_from_worker(new Worker('readable-stream-byob-request.js'));
-</script>
index 8764da7..e907a1e 100644 (file)
@@ -1,37 +1,37 @@
 
 PASS Running templatedRSClosed with ReadableStream (closed via call in start) 
-PASS piping to a WritableStream in the writable state should close the writable stream 
-PASS piping to a WritableStream in the writable state with { preventClose: true } should do nothing 
+PASS ReadableStream (closed via call in start): piping to a WritableStream in the writable state should close the writable stream 
+PASS ReadableStream (closed via call in start): piping to a WritableStream in the writable state with { preventClose: true } should do nothing 
 PASS Running templatedRSClosed with ReadableStream (closed via cancel) 
-PASS piping to a WritableStream in the writable state should close the writable stream 
-PASS piping to a WritableStream in the writable state with { preventClose: true } should do nothing 
+PASS ReadableStream (closed via cancel): piping to a WritableStream in the writable state should close the writable stream 
+PASS ReadableStream (closed via cancel): piping to a WritableStream in the writable state with { preventClose: true } should do nothing 
 PASS Running templatedRSErrored with ReadableStream (errored via call in start) 
-PASS piping to a WritableStream in the writable state should abort the writable stream 
+PASS ReadableStream (errored via call in start): piping to a WritableStream in the writable state should abort the writable stream 
 PASS Running templatedRSErrored with ReadableStream (errored via returning a rejected promise in start) 
-PASS piping to a WritableStream in the writable state should abort the writable stream 
+PASS ReadableStream (errored via returning a rejected promise in start): piping to a WritableStream in the writable state should abort the writable stream 
 PASS Running templatedRSErroredAsyncOnly with ReadableStream (errored via returning a rejected promise in start) reader 
-PASS piping with no options 
-PASS piping with { preventAbort: false } 
-PASS piping with { preventAbort: true } 
+PASS ReadableStream (errored via returning a rejected promise in start) reader: piping with no options 
+PASS ReadableStream (errored via returning a rejected promise in start) reader: piping with { preventAbort: false } 
+PASS ReadableStream (errored via returning a rejected promise in start) reader: piping with { preventAbort: true } 
 PASS Running templatedRSTwoChunksClosed with ReadableStream (two chunks enqueued, then closed) 
-PASS piping with no options and no destination errors 
-PASS piping with { preventClose: false } and no destination errors 
-PASS piping with { preventClose: true } and no destination errors 
-PASS piping with { preventClose: false } and a destination with that errors synchronously 
-PASS piping with { preventClose: true } and a destination with that errors synchronously 
-PASS piping with { preventClose: true } and a destination that errors on the last chunk 
+PASS ReadableStream (two chunks enqueued, then closed): piping with no options and no destination errors 
+PASS ReadableStream (two chunks enqueued, then closed): piping with { preventClose: false } and no destination errors 
+PASS ReadableStream (two chunks enqueued, then closed): piping with { preventClose: true } and no destination errors 
+PASS ReadableStream (two chunks enqueued, then closed): piping with { preventClose: false } and a destination with that errors synchronously 
+PASS ReadableStream (two chunks enqueued, then closed): piping with { preventClose: true } and a destination with that errors synchronously 
+PASS ReadableStream (two chunks enqueued, then closed): piping with { preventClose: true } and a destination that errors on the last chunk 
 PASS Running templatedRSTwoChunksClosed with ReadableStream (two chunks enqueued async, then closed) 
-PASS piping with no options and no destination errors 
-PASS piping with { preventClose: false } and no destination errors 
-PASS piping with { preventClose: true } and no destination errors 
-PASS piping with { preventClose: false } and a destination with that errors synchronously 
-PASS piping with { preventClose: true } and a destination with that errors synchronously 
-PASS piping with { preventClose: true } and a destination that errors on the last chunk 
+PASS ReadableStream (two chunks enqueued async, then closed): piping with no options and no destination errors 
+PASS ReadableStream (two chunks enqueued async, then closed): piping with { preventClose: false } and no destination errors 
+PASS ReadableStream (two chunks enqueued async, then closed): piping with { preventClose: true } and no destination errors 
+PASS ReadableStream (two chunks enqueued async, then closed): piping with { preventClose: false } and a destination with that errors synchronously 
+PASS ReadableStream (two chunks enqueued async, then closed): piping with { preventClose: true } and a destination with that errors synchronously 
+PASS ReadableStream (two chunks enqueued async, then closed): piping with { preventClose: true } and a destination that errors on the last chunk 
 PASS Running templatedRSTwoChunksClosed with ReadableStream (two chunks enqueued via pull, then closed) 
-PASS piping with no options and no destination errors 
-PASS piping with { preventClose: false } and no destination errors 
-PASS piping with { preventClose: true } and no destination errors 
-PASS piping with { preventClose: false } and a destination with that errors synchronously 
-PASS piping with { preventClose: true } and a destination with that errors synchronously 
-PASS piping with { preventClose: true } and a destination that errors on the last chunk 
+PASS ReadableStream (two chunks enqueued via pull, then closed): piping with no options and no destination errors 
+PASS ReadableStream (two chunks enqueued via pull, then closed): piping with { preventClose: false } and no destination errors 
+PASS ReadableStream (two chunks enqueued via pull, then closed): piping with { preventClose: true } and no destination errors 
+PASS ReadableStream (two chunks enqueued via pull, then closed): piping with { preventClose: false } and a destination with that errors synchronously 
+PASS ReadableStream (two chunks enqueued via pull, then closed): piping with { preventClose: true } and a destination with that errors synchronously 
+PASS ReadableStream (two chunks enqueued via pull, then closed): piping with { preventClose: true } and a destination that errors on the last chunk 
 
index 5bca6ae..7f0797e 100644 (file)
@@ -16,7 +16,7 @@ function templatedRSClosed(label, factory) {
     test(function() {
     }, 'Running templatedRSClosed with ' + label);
 
-    var test1 = async_test('piping to a WritableStream in the writable state should close the writable stream');
+    var test1 = async_test(label + ': piping to a WritableStream in the writable state should close the writable stream');
     test1.step(function() {
         var closeCalled = false;
 
@@ -49,7 +49,7 @@ function templatedRSClosed(label, factory) {
         })).catch(test1.step_func(function(e) { assert_unreached(e); }));
     });
 
-    var test2 = async_test('piping to a WritableStream in the writable state with { preventClose: true } should do nothing');
+    var test2 = async_test(label + ': piping to a WritableStream in the writable state with { preventClose: true } should do nothing');
     test2.step(function() {
         var rs = factory();
 
@@ -84,7 +84,7 @@ function templatedRSErrored(label, factory, error) {
     test(function() {
     }, 'Running templatedRSErrored with ' + label);
 
-    var test1 = async_test('piping to a WritableStream in the writable state should abort the writable stream');
+    var test1 = async_test(label + ': piping to a WritableStream in the writable state should abort the writable stream');
     test1.step(function() {
         var rs = factory();
 
@@ -123,7 +123,7 @@ function templatedRSErroredAsyncOnly(label, factory, error) {
     test(function() {
     }, 'Running templatedRSErroredAsyncOnly with ' + label);
 
-    var test1 = async_test('piping with no options');
+    var test1 = async_test(label + ': piping with no options');
     test1.step(function() {
         var closeCalled = false;
 
@@ -148,7 +148,7 @@ function templatedRSErroredAsyncOnly(label, factory, error) {
         }))
     });
 
-    var test2 = async_test('piping with { preventAbort: false }');
+    var test2 = async_test(label + ': piping with { preventAbort: false }');
     test2.step(function() {
         var abortCalled = false;
         var closeRejected = false;
@@ -176,7 +176,7 @@ function templatedRSErroredAsyncOnly(label, factory, error) {
         }));
     });
 
-    var test3 = async_test('piping with { preventAbort: true }');
+    var test3 = async_test(label + ': piping with { preventAbort: true }');
     test3.step(function() {
         var rs = factory();
 
@@ -198,7 +198,7 @@ function templatedRSTwoChunksClosed(label, factory, error) {
     test(function() {
     }, 'Running templatedRSTwoChunksClosed with ' + label);
 
-    var test1 = async_test('piping with no options and no destination errors');
+    var test1 = async_test(label + ': piping with no options and no destination errors');
     test1.step(function() {
         var rs = factory();
 
@@ -219,7 +219,7 @@ function templatedRSTwoChunksClosed(label, factory, error) {
         }));
     });
 
-    var test2 = async_test('piping with { preventClose: false } and no destination errors');
+    var test2 = async_test(label + ': piping with { preventClose: false } and no destination errors');
     test2.step(function() {
         var rs = factory();
 
@@ -240,7 +240,7 @@ function templatedRSTwoChunksClosed(label, factory, error) {
         }));
     });
 
-    var test3 = async_test('piping with { preventClose: true } and no destination errors');
+    var test3 = async_test(label + ': piping with { preventClose: true } and no destination errors');
     test3.step(function() {
         var rs = factory();
 
@@ -264,7 +264,7 @@ function templatedRSTwoChunksClosed(label, factory, error) {
         }));
     });
 
-    var test4 = async_test('piping with { preventClose: false } and a destination with that errors synchronously');
+    var test4 = async_test(label + ': piping with { preventClose: false } and a destination with that errors synchronously');
     test4.step(function() {
         var rs = factory();
 
@@ -290,7 +290,7 @@ function templatedRSTwoChunksClosed(label, factory, error) {
         );
     });
 
-    var test5 = async_test('piping with { preventClose: true } and a destination with that errors synchronously');
+    var test5 = async_test(label + ': piping with { preventClose: true } and a destination with that errors synchronously');
     test5.step(function() {
         var rs = factory();
 
@@ -316,7 +316,7 @@ function templatedRSTwoChunksClosed(label, factory, error) {
         );
     });
 
-    var test6 = async_test('piping with { preventClose: true } and a destination that errors on the last chunk');
+    var test6 = async_test(label + ': piping with { preventClose: true } and a destination that errors on the last chunk');
     test6.step(function() {
         var rs = factory();
 
index 917b8d7..4ae954a 100644 (file)
@@ -1,32 +1,32 @@
 CONSOLE MESSAGE: Unhandled Promise Rejection: a
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
 CONSOLE MESSAGE: Unhandled Promise Rejection: Error: Sorry, it just wasn't meant to be.
 CONSOLE MESSAGE: Unhandled Promise Rejection: Error: Sorry, it just wasn't meant to be.
 CONSOLE MESSAGE: Unhandled Promise Rejection: Error: Sorry, it just wasn't meant to be.
 CONSOLE MESSAGE: Unhandled Promise Rejection: Error: Sorry, it just wasn't meant to be.
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
 
-Harness Error (FAIL), message = TypeError: undefined is not an object (evaluating 'e.message')
+Harness Error (FAIL), message = TypeError: undefined is not an object (evaluating 'e.error')
 
 PASS Aborting a WritableStream immediately prevents future writes 
 PASS Aborting a WritableStream prevents further writes after any that are in progress 
index 9258277..6b2f306 100644 (file)
@@ -1,7 +1,7 @@
-CONSOLE MESSAGE: line 2687: TypeError: undefined is not an object (evaluating 'e.message')
+CONSOLE MESSAGE: line 3374: TypeError: undefined is not an object (evaluating 'e.error')
 CONSOLE MESSAGE: Unhandled Promise Rejection: undefined
 
-Harness Error (FAIL), message = TypeError: undefined is not an object (evaluating 'e.message')
+Harness Error (FAIL), message = TypeError: undefined is not an object (evaluating 'e.error')
 
 PASS error argument is given to start method 
 PASS Underlying sink's write won't be called until start finishes 
index b8ebe43..b96bd59 100644 (file)
@@ -2,6 +2,6 @@
 PASS Basic data channel exchange from offerer to receiver 
 PASS Basic data channel exchange from receiver to offerer 
 PASS Basic data channel exchange from offerer to receiver using UDP only 
-PASS Basic data channel exchange from offerer to receiver 
+PASS Basic data channel exchange from offerer to receiver 
 PASS Create a second channel asynchronously and send messages 
 
index fb9f4db..2e19687 100644 (file)
@@ -124,7 +124,7 @@ promise_test((test) => {
         });
         setTimeout(() => { reject("Test timed out"); }, 5000);
     });
-}, "Basic data channel exchange from offerer to receiver");
+}, "Basic data channel exchange from offerer to receiver 2");
 
 promise_test(async (test) => {
     await new Promise((resolve, reject) => {
index f74e86c..87e155d 100644 (file)
@@ -3,5 +3,5 @@ PASS Getting some MDNS candidates
 PASS Basic data channel exchange from offerer to receiver 
 PASS Basic data channel exchange from receiver to offerer 
 PASS Basic data channel exchange from offerer to receiver using UDP only 
-PASS Basic data channel exchange from offerer to receiver 
+PASS Basic data channel exchange from offerer to receiver 
 
index 64ecb80..0665e9d 100644 (file)
@@ -147,7 +147,7 @@ promise_test((test) => {
         });
         setTimeout(() => { reject("Test timed out"); }, 5000);
     });
-}, "Basic data channel exchange from offerer to receiver");
+}, "Basic data channel exchange from offerer to receiver 2");
 
     </script>
   </body>
index 89cbf4b..2233812 100644 (file)
@@ -1,4 +1,4 @@
 
 PASS Peer Connection objects should not be created in detached documents 
-PASS Peer Connection objects should not be created in detached documents 
+PASS Peer Connection objects should not be created in detached documents 
 
index 7101e6d..9fecf22 100644 (file)
@@ -19,7 +19,7 @@ test(() => {
     var pc = new frame.contentWindow.RTCPeerConnection();
     document.body.removeChild(frame);
     assert_throws('InvalidStateError', () => { pc.setConfiguration({}) });
-}, "Peer Connection objects should not be created in detached documents");
+}, "Peer Connection objects should not be created in detached documents 2");
 </script>
 </body>
 </html>
index 0a25875..cd57878 100644 (file)
@@ -7,5 +7,5 @@ PASS Ensuring connection state is connected
 PASS Track is enabled, video should not be black 
 PASS Track is disabled, video should be black 
 PASS If disabled, black frames should still be coming 
-PASS Track is enabled, video should not be black 
+PASS Track is enabled, video should not be black 
 
index 6949db4..649b70d 100644 (file)
@@ -8,5 +8,5 @@ PASS Ensuring connection state is connected
 PASS Track is enabled, video should not be black 
 PASS Track is disabled, video should be black 
 PASS If disabled, black frames should still be coming 
-PASS Track is enabled, video should not be black 
+PASS Track is enabled, video should not be black 
 
index d614193..e87b498 100644 (file)
@@ -119,7 +119,7 @@ promise_test((test) => {
 promise_test((test) => {
     track.enabled = true;
     return checkVideoBlack(false, canvas2, video);
-}, "Track is enabled, video should not be black");
+}, "Track is enabled, video should not be black 2");
 
         </script>
     </body>
index f279c13..6918b4e 100644 (file)
@@ -101,7 +101,7 @@ promise_test((test) => {
 promise_test((test) => {
     track.enabled = true;
     return checkVideoBlack(false, canvas2, video);
-}, "Track is enabled, video should not be black");
+}, "Track is enabled, video should not be black 2");
 
         </script>
     </body>