chromium/third_party/blink/web_tests/wpt_internal/fetch/api/body/load-from-detached-iframe.window.js

'use strict';

// Tests that attempting to load a Response body from a detached iframe fails
// correctly. Blink's behavior of rejecting the promise is non-standard, so this
// is not included in the upstream wpts.

// Each case exercises a different method on the body. `method` is the name of
// the method to call. `type` is the Content-Type for the response. `body` is
// the body of the response. It should parse correctly into the specified
// format. Because it is interpolated into JavaScript it should not contain
// quote or backslash characters.
const CASES = [
  {
    method: 'arrayBuffer',
    type: 'application/octet-stream',
    body: 'anything',
    expected_type: 'ArrayBuffer',
  },
  {
    method: 'blob',
    type: 'application/octet-stream',
    body: 'anything',
    expected_type: 'Blob',
  },
  {
    method: 'formData',
    type: 'application/x-www-form-urlencoded',
    body: 'a=b',
    expected_type: 'FormData',
  },
  {
    method: 'json',
    type: 'text/json',
    body: '[3]',
    expected_type: 'Array',
  },
  {
    method: 'text',
    text: 'text/plain',
    body: 'word',
    expected_type: 'string',
  },
];

function assert_type_is(value, expected_type, description) {
  let actual_type = typeof value;
  if (actual_type === 'object') {
    actual_type = value.constructor.name;
  }
  assert_equals(actual_type, expected_type, description);
}

// First just check that the test cases work at all, so we don't fail for the
// wrong reasons.
for (const {method, type, body, expected_type} of CASES) {
  promise_test(async () => {
    const response = new Response(body, { headers: { 'Content-Type': type } });
    const result = await response[method]();
    assert_not_equals(result, undefined, 'result should be defined');
    assert_not_equals(result, null, 'result should not be null');
    assert_type_is(result, expected_type, `result should be ${expected_type}`);
  }, `decoding a response with method ${method} should work`);
}

// Now verify that they reject correctly when the body was created in an iframe
// that has been detached.

// We can wait for this to make sure the document has a body.
const loaded = new Promise(resolve => {
  window.onload = resolve;
});

async function waitForDoneMessage(t) {
  const watcher = new EventWatcher(t, window, [ 'message' ]);
  const evt = await watcher.wait_for([ 'message' ]);
  if (evt.data != 'done') {
    throw RangeError('bad message');
  }
}

async function appendIFrame(t, body, type) {
  await loaded;
  const iframe = document.createElement('iframe');
  iframe.srcdoc = `
<script>
    window.response = new Response('${body}',
                                   { headers: { 'Content-Type': '${type}' } });
    parent.postMessage('done', '*');
</script>
`;
  document.body.appendChild(iframe);
  await waitForDoneMessage(t);
  return iframe;
}

for (const {method, type, body} of CASES) {
  promise_test(async t => {
    const iframe = await appendIFrame(t, body, type);
    const response = iframe.contentWindow.response;
    const IFrameDOMException = iframe.contentWindow.DOMException;
    iframe.remove();
    await promise_rejects_dom(t, 'InvalidStateError', IFrameDOMException,
                              response[method](), `${method}() should reject`);
  }, `decoding a response from a detached frame with method ${method} ` +
               'should reject');
}

promise_test(async t => {
  const iframe = await appendIFrame(t, 'chunk', 'text/plain');
  const response = iframe.contentWindow.response;
  const IFrameTypeError = iframe.contentWindow.TypeError;
  iframe.remove();
  const reader = response.body.getReader();
  await promise_rejects_js(t, IFrameTypeError, reader.read(),
                           'read() should reject');
}, 'reading a response from a detached frame as a stream should reject');