1<!DOCTYPE html>
2<meta charset="utf-8">
3<script src="/resources/testharness.js"></script>
4<script src="/resources/testharnessreport.js"></script>
5<script src="resources/helpers.js"></script>
6<script>
7'use strict';
8
9// Chrome used to special-case the reason for cancel() and abort() in order to
10// handle exceptions correctly. This is no longer necessary. These tests are
11// retained to avoid regressions.
12
13async function getTransferredReason(originalReason) {
14  let resolvePromise;
15  const rv = new Promise(resolve => {
16    resolvePromise = resolve;
17  });
18  const rs = await createTransferredReadableStream({
19    cancel(reason) {
20      resolvePromise(reason);
21    }
22  });
23  await rs.cancel(originalReason);
24  return rv;
25}
26
27for (const value of ['hi', '\t\r\n', 7, 3.0, undefined, null, true, false,
28                    NaN, Infinity]) {
29  promise_test(async () => {
30    const reason = await getTransferredReason(value);
31    assert_equals(reason, value, 'reason should match');
32  }, `reason with a simple value of '${value}' should be preserved`);
33}
34
35for (const badType of [Symbol('hi'), _ => 'hi']) {
36  promise_test(async t => {
37    return promise_rejects_dom(t, 'DataCloneError',
38                               getTransferredReason(badType),
39                               'cancel() should reject');
40  }, `reason with a type of '${typeof badType}' should be rejected and ` +
41     `error the stream`);
42}
43
44promise_test(async () => {
45  const reasonAsJson =
46        `{"foo":[1,"col"],"bar":{"hoge":0.2,"baz":{},"shan":null}}`;
47  const reason = await getTransferredReason(JSON.parse(reasonAsJson));
48  assert_equals(JSON.stringify(reason), reasonAsJson,
49                'object should be preserved');
50}, 'objects that can be completely expressed in JSON should be preserved');
51
52promise_test(async () => {
53  const circularObject = {};
54  circularObject.self = circularObject;
55  const reason = await getTransferredReason(circularObject);
56  assert_true(reason instanceof Object, 'an Object should be output');
57  assert_equals(reason.self, reason,
58                'the object should have a circular reference');
59}, 'objects that cannot be expressed in JSON should also be preserved');
60
61promise_test(async () => {
62  const originalReason = new TypeError('hi');
63  const reason = await getTransferredReason(originalReason);
64  assert_true(reason instanceof TypeError,
65              'type should be preserved');
66  assert_equals(reason.message, originalReason.message,
67                'message should be preserved');
68}, 'the type and message of a TypeError should be preserved');
69
70promise_test(async () => {
71  const originalReason = new TypeError('hi');
72  originalReason.foo = 'bar';
73  const reason = await getTransferredReason(originalReason);
74  assert_false('foo' in reason,
75               'foo should not be preserved');
76}, 'other attributes of a TypeError should not be preserved');
77
78promise_test(async () => {
79  const originalReason = new TypeError();
80  originalReason.message = [1, 2, 3];
81  const reason = await getTransferredReason(originalReason);
82  assert_equals(reason.message, '1,2,3', 'message should be stringified');
83}, 'a TypeError message should be converted to a string');
84
85promise_test(async () => {
86  const originalReason = new TypeError();
87  Object.defineProperty(originalReason, 'message', {
88    get() { return 'words'; }
89  });
90  const reason = await getTransferredReason(originalReason);
91  assert_equals(reason.message, '', 'message should not be preserved');
92}, 'a TypeError message should not be preserved if it is a getter');
93
94promise_test(async () => {
95  const originalReason = new TypeError();
96  delete originalReason.message;
97  TypeError.prototype.message = 'inherited message';
98  const reason = await getTransferredReason(originalReason);
99  delete TypeError.prototype.message;
100  assert_equals(reason.message, '', 'message should not be preserved');
101}, 'a TypeError message should not be preserved if it is inherited');
102
103promise_test(async () => {
104  const originalReason = new DOMException('yes', 'AbortError');
105  const reason = await getTransferredReason(originalReason);
106  assert_true(reason instanceof DOMException,
107              'reason should be a DOMException');
108  assert_equals(reason.message, originalReason.message,
109                'the messages should match');
110  assert_equals(reason.name, originalReason.name,
111                'the names should match');
112}, 'DOMException errors should be preserved');
113
114for (const errorConstructor of [EvalError, RangeError,
115                                ReferenceError, SyntaxError, TypeError,
116                                URIError]) {
117  promise_test(async () => {
118    const originalReason = new errorConstructor('nope');
119    const reason = await getTransferredReason(originalReason);
120    assert_equals(typeof reason, 'object', 'reason should have type object');
121    assert_true(reason instanceof errorConstructor,
122                `reason should inherit ${errorConstructor.name}`);
123    assert_true(reason instanceof Error, 'reason should inherit Error');
124    assert_equals(reason.constructor, errorConstructor,
125                  'reason should have the right constructor');
126    assert_equals(reason.name, errorConstructor.name,
127                  `name should match constructor name`);
128    assert_equals(reason.message, 'nope', 'message should match');
129  }, `${errorConstructor.name} should be preserved`);
130}
131
132</script>
133