Skip to content

Commit

Permalink
buffer: make Blob's slice method more spec-compliant
Browse files Browse the repository at this point in the history
PR-URL: nodejs#37361
Backport-PR-URL: nodejs#39704
Fixes: nodejs#37335
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
targos authored and foxxyz committed Oct 18, 2021
1 parent d573bea commit ea81e60
Show file tree
Hide file tree
Showing 14 changed files with 1,355 additions and 28 deletions.
44 changes: 30 additions & 14 deletions lib/internal/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const {
ArrayFrom,
MathMax,
MathMin,
ObjectDefineProperty,
ObjectSetPrototypeOf,
PromiseResolve,
Expand Down Expand Up @@ -43,21 +45,21 @@ const {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_BUFFER_TOO_LARGE,
ERR_OUT_OF_RANGE,
}
} = require('internal/errors');

const {
validateObject,
validateString,
validateUint32,
isUint32,
} = require('internal/validators');

const kHandle = Symbol('kHandle');
const kType = Symbol('kType');
const kLength = Symbol('kLength');

const disallowedTypeCharacters = /[^\u{0020}-\u{007E}]/u;

let Buffer;

function lazyBuffer() {
Expand Down Expand Up @@ -141,7 +143,7 @@ class Blob extends JSTransferable {
super();
this[kHandle] = createBlob(sources_, length);
this[kLength] = length;
this[kType] = RegExpPrototypeTest(/[^\u{0020}-\u{007E}]/u, type) ?
this[kType] = RegExpPrototypeTest(disallowedTypeCharacters, type) ?
'' : StringPrototypeToLowerCase(type);
}

Expand Down Expand Up @@ -180,18 +182,32 @@ class Blob extends JSTransferable {

get size() { return this[kLength]; }

slice(start = 0, end = (this[kLength]), type = this[kType]) {
validateUint32(start, 'start');
if (end < 0) end = this[kLength] + end;
validateUint32(end, 'end');
validateString(type, 'type');
if (end < start)
throw new ERR_OUT_OF_RANGE('end', 'greater than start', end);
if (end > this[kLength])
throw new ERR_OUT_OF_RANGE('end', 'less than or equal to length', end);
slice(start = 0, end = this[kLength], contentType = '') {
if (start < 0) {
start = MathMax(this[kLength] + start, 0);
} else {
start = MathMin(start, this[kLength]);
}
start |= 0;

if (end < 0) {
end = MathMax(this[kLength] + end, 0);
} else {
end = MathMin(end, this[kLength]);
}
end |= 0;

contentType = `${contentType}`;
if (RegExpPrototypeTest(disallowedTypeCharacters, contentType)) {
contentType = '';
} else {
contentType = StringPrototypeToLowerCase(contentType);
}

const span = MathMax(end - start, 0);

return new InternalBlob(
this[kHandle].slice(start, end),
end - start, type);
this[kHandle].slice(start, start + span), span, contentType);
}

async arrayBuffer() {
Expand Down
53 changes: 53 additions & 0 deletions test/fixtures/wpt/FileAPI/blob/Blob-constructor-dom.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// META: title=Blob constructor
// META: script=../support/Blob.js
'use strict';

var test_error = {
name: "test",
message: "test error",
};

test(function() {
var args = [
document.createElement("div"),
window,
];
args.forEach(function(arg) {
assert_throws_js(TypeError, function() {
new Blob(arg);
}, "Should throw for argument " + format_value(arg) + ".");
});
}, "Passing platform objects for blobParts should throw a TypeError.");

test(function() {
var element = document.createElement("div");
element.appendChild(document.createElement("div"));
element.appendChild(document.createElement("p"));
var list = element.children;
Object.defineProperty(list, "length", {
get: function() { throw test_error; }
});
assert_throws_exactly(test_error, function() {
new Blob(list);
});
}, "A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)");

test_blob(function() {
var select = document.createElement("select");
select.appendChild(document.createElement("option"));
return new Blob(select);
}, {
expected: "[object HTMLOptionElement]",
type: "",
desc: "Passing an platform object that supports indexed properties as the blobParts array should work (select)."
});

test_blob(function() {
var elm = document.createElement("div");
elm.setAttribute("foo", "bar");
return new Blob(elm.attributes);
}, {
expected: "[object Attr]",
type: "",
desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)."
});
Loading

0 comments on commit ea81e60

Please sign in to comment.