Last active
December 7, 2016 22:55
-
-
Save mkruisselbrink/2dbfb08082699fe593d6047491666667 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset=utf-8> | |
<title>Blob.close</title> | |
<script src="/resources/testharness.js"></script> | |
<script src="/resources/testharnessreport.js"></script> | |
<script> | |
if (!Blob.prototype.close && Blob.prototype.msClose) { | |
// Edge has msClose rather than close. | |
Blob.prototype.close = Blob.prototype.msClose; | |
// Edge also doesn't have isClosed, approximate it with size == 0 | |
Object.defineProperty(Blob.prototype, 'isClosed', { | |
get: function() { return this.size == 0; } | |
}); | |
// Edge throws different types of exceptions, so for now just ignore | |
// the exact type it throws. | |
const oldAssertThrows = assert_throws; | |
assert_throws = function(e, f, d) { return oldAssertThrows(null, f, d); }; | |
const oldPromiseRejects = promise_rejects; | |
promise_rejects = function(t, e, p, d) { return oldPromiseRejects(t, null, p, d); }; | |
} | |
// Edge doesn't implement FormData.get, so polyfill it by parsing | |
// the textual output of FormData. | |
function parseFormDataText(s) { | |
const lines = s.split('\r\n'); | |
const boundary = lines[0]; | |
let props = []; | |
let current = null; | |
let inHeaders = false; | |
for (let line of lines) { | |
if (line.startsWith(boundary)) { | |
inHeaders = true; | |
if (current) { | |
current.value = current.value.substr(0, current.value.length-1); | |
props.push(current); | |
} | |
current = {value : ''}; | |
continue; | |
} | |
if (inHeaders) { | |
if (line == '') { | |
inHeaders = false; | |
continue; | |
} | |
if (line.startsWith('Content-Disposition')) { | |
let params = line.split(';'); | |
for (let param of params) { | |
let kv = param.trim().split('='); | |
if (kv[0] == 'name') | |
current.name = kv[1].substr(1, kv[1].length - 2); | |
} | |
} | |
continue; | |
} | |
current.value = current.value + line + '\n'; | |
} | |
return props; | |
} | |
// Wrapper around FormData.get that returns a promise. | |
function FormDataGet(fd, name) { | |
if (FormData.prototype.get) { | |
return Promise.resolve(fd.get(name)); | |
} else { | |
return new Response(fd).text().then(t => { | |
let props = parseFormDataText(t); | |
for (let p of props) { | |
if (p.name == name) return new Blob([p.value]); | |
} | |
return null; | |
}); | |
} | |
} | |
function readBlob(blob) { | |
return new Promise((resolve, reject) => { | |
const reader = new FileReader(); | |
reader.onerror = () => reject(reader.error); | |
reader.onload = () => resolve(reader.result); | |
reader.readAsText(blob, 'UTF-8'); | |
}); | |
} | |
function unreached_rejection(test, prefix) { | |
return test.step_func(function(error) { | |
var reason = error.message || error.name || error; | |
var error_prefix = prefix || 'unexpected rejection'; | |
assert_unreached(error_prefix + ': ' + reason); | |
}); | |
} | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
assert_false(blob.isClosed); | |
blob.close(); | |
assert_equals(0, blob.size); | |
assert_true(blob.isClosed); | |
}, 'A closed blob has correct attributes.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
const reader = new FileReader(); | |
assert_throws('InvalidStateError', () => reader.readAsText(blob, 'UTF-8')); | |
}, 'Reading a closed blob should fail.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
assert_throws('InvalidStateError', () => blob.slice()); | |
}, 'Slicing a closed blob should fail.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
assert_throws('InvalidStateError', () => new Blob([blob])); | |
}, 'Referencing a closed blob should fail.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
const url = URL.createObjectURL(blob); | |
blob.close(); | |
return promise_rejects(t, new TypeError, fetch(url)); | |
}, 'Can\'t read a closed blob through a previously created URL.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
assert_throws('InvalidStateError', () => URL.createObjectURL(blob)); | |
}, 'Can\'t create a blob URL for a closed blob.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
const sliced = blob.slice(); | |
blob.close(); | |
return readBlob(sliced).then(result => assert_equals(result, 'TEST')); | |
}, 'Slice should still have blob data.'); | |
async_test(t => { | |
const blob = new Blob(["TEST"]); | |
const channel = new MessageChannel(); | |
channel.port1.onmessage = t.step_func(e => { | |
const cloned = e.data; | |
assert_true(blob.isClosed); | |
assert_false(cloned.isClosed); | |
readBlob(cloned).then(t.step_func_done(result => assert_equals(result, 'TEST'))).catch(unreached_rejection(t)); | |
}); | |
channel.port2.postMessage(blob); | |
blob.close(); | |
}, 'Clone should still have blob data.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
const channel = new MessageChannel(); | |
assert_throws('DataCloneError', () => channel.port2.postMessage(blob)); | |
}, 'Cloning after closing should not be possible.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
const request = new Request('http://example.com', {method: 'POST', body: blob}) | |
return request.text().then(text => assert_equals(text, '')); | |
}, 'Creating a request with a closed blob should not fail.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
const request = new Request('/XMLHttpRequest/resources/content.py', {method: 'POST', body: blob}) | |
return fetch(request).then(response => response.text()).then(text => assert_equals(text, '')); | |
}, 'Fetching a request with a closed blob should not fail.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
const response = new Response(blob); | |
return response.text().then(text => assert_equals(text, '')); | |
}, 'Creating a response with a closed blob should not fail.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
const request = new Request('http://example.com', {method: 'POST', body: blob}); | |
blob.close(); | |
return request.text().then(text => assert_equals(text, 'TEST')); | |
}, 'Request can still read body after closing blob.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
const response = new Response(blob); | |
blob.close(); | |
return response.text().then(text => assert_equals(text, 'TEST')); | |
}, 'Response can still read body after closing blob.'); | |
test(t => { | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
const data = new FormData(); | |
assert_throws('InvalidStateError', () => data.append('foo', blob)); | |
assert_throws('InvalidStateError', () => data.set('foo', blob)); | |
}, 'FormData should throw when using closed blobs.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
const data = new FormData(); | |
data.append('foo', blob); | |
blob.close(); | |
return FormDataGet(data, 'foo').then(b => { | |
assert_false(b.isClosed); | |
return readBlob(b); | |
}).then(result => assert_equals(result, 'TEST')); | |
}, 'FormData should keep data after closing blobs.'); | |
promise_test(t => { | |
const blob = new Blob(["TEST"]); | |
const data = new FormData(); | |
data.append('foo', blob); | |
return FormDataGet(data, 'foo').then(b => { | |
b.close(); | |
return FormDataGet(data, 'foo'); | |
}).then(b => { | |
assert_false(b.isClosed); | |
return readBlob(b); | |
}).then(result => assert_equals(result, 'TEST')); | |
}, 'Closing result of FormData.get() should not clear out data.'); | |
async_test(t => { | |
const xhr = new XMLHttpRequest(); | |
const blob = new Blob(["TEST"]); | |
blob.close(); | |
xhr.onreadystatechange = t.step_func(() => { | |
if (xhr.readyState == 4) { | |
assert_equals(xhr.status, 200); | |
assert_equals(xhr.response, ""); | |
t.done(); | |
} | |
}); | |
xhr.open("POST", "/XMLHttpRequest/resources/content.py"); | |
xhr.send(blob); | |
}, 'Sending closed blob using XHR.'); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment