Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 51 additions & 51 deletions builtins/web/blob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,56 @@ template <typename T> bool validate_type(T *chars, size_t strlen) {
return true;
}


// https://w3c.github.io/FileAPI/#convert-line-endings-to-native
std::string convert_line_endings_to_native(std::string_view s) {
std::string native_line_ending = "\n";
#ifdef _WIN32
native_line_ending = "\r\n";
#endif

std::string result;
result.reserve(s.size());

size_t i = 0;
while (i < s.size()) {
switch (s[i]) {
case '\r': {
if (i + 1 < s.size() && s[i + 1] == '\n') {
result.append(native_line_ending);
i += 2;
} else {
result.append(native_line_ending);
i += 1;
}
break;
}
case '\n': {
result.append(native_line_ending);
i += 1;
break;
}
default: {
result.push_back(s[i]);
i += 1;
break;
}
}
}

return result;
}

} // anonymous namespace



namespace builtins::web::blob {

// https://w3c.github.io/FileAPI/#dfn-type
// 1. If type contains any characters outside the range U+0020 to U+007E, then set t to the empty string.
// 2. Convert every character in type to ASCII lowercase.
JSString *normalize_type(JSContext *cx, HandleValue value) {
JSString *Blob::normalize_type(JSContext *cx, JS::HandleValue value) {
JS::RootedString value_str(cx);

if (value.isObject() || value.isString()) {
Expand Down Expand Up @@ -60,15 +107,13 @@ JSString *normalize_type(JSContext *cx, HandleValue value) {
if (!validate_type(chars, strlen)) {
return JS_GetEmptyString(cx);
}

normalized = std::string(reinterpret_cast<const char *>(chars), strlen);
} else {
JS::AutoCheckCannotGC nogc(cx);
const auto *chars = (JS::GetTwoByteLinearStringChars(nogc, str));
const auto *chars = JS::GetTwoByteLinearStringChars(nogc, str);
if (!validate_type(chars, strlen)) {
return JS_GetEmptyString(cx);
}

normalized.reserve(strlen);
for (size_t i = 0; i < strlen; ++i) {
normalized += static_cast<unsigned char>(chars[i]);
Expand All @@ -81,51 +126,6 @@ JSString *normalize_type(JSContext *cx, HandleValue value) {
return JS_NewStringCopyN(cx, normalized.c_str(), normalized.length());
}

// https://w3c.github.io/FileAPI/#convert-line-endings-to-native
std::string convert_line_endings_to_native(std::string_view s) {
std::string native_line_ending = "\n";
#ifdef _WIN32
native_line_ending = "\r\n";
#endif

std::string result;
result.reserve(s.size());

size_t i = 0;
while (i < s.size()) {
switch (s[i]) {
case '\r': {
if (i + 1 < s.size() && s[i + 1] == '\n') {
result.append(native_line_ending);
i += 2;
} else {
result.append(native_line_ending);
i += 1;
}
break;
}
case '\n': {
result.append(native_line_ending);
i += 1;
break;
}
default: {
result.push_back(s[i]);
i += 1;
break;
}
}
}

return result;
}

} // anonymous namespace



namespace builtins::web::blob {

using js::Vector;
using streams::BufReader;
using streams::NativeStreamSource;
Expand Down Expand Up @@ -285,7 +285,7 @@ bool Blob::slice(JSContext *cx, HandleObject self, const CallArgs &args, Mutable

if (args.hasDefined(2)) {
HandleValue contentType_val = args.get(2);
if ((contentType = normalize_type(cx, contentType_val)) == nullptr) {
if ((contentType = Blob::normalize_type(cx, contentType_val)) == nullptr) {
return false;
}
}
Expand Down Expand Up @@ -547,7 +547,7 @@ bool Blob::init_options(JSContext *cx, HandleObject self, HandleValue initv) {
return false;
}

auto *type_str = normalize_type(cx, type);
auto *type_str = Blob::normalize_type(cx, type);
if (!type_str) {
return false;
}
Expand Down
1 change: 1 addition & 0 deletions builtins/web/blob.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Blob : public BuiltinImpl<Blob, FinalizableClassPolicy> {
static bool read_blob_slice(JSContext *cx, HandleObject self, std::span<uint8_t> buf,
size_t start, size_t *read, bool *done);

static JSString *normalize_type(JSContext *cx, JS::HandleValue value);
static JSObject *create(JSContext *cx, UniqueChars data, size_t data_len, HandleString type);

static bool init_class(JSContext *cx, HandleObject global);
Expand Down
26 changes: 25 additions & 1 deletion builtins/web/fetch/request-response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,32 @@ bool RequestOrResponse::parse_body(JSContext *cx, JS::HandleObject self, JS::Uni
result.setObject(*array_buffer);
} else if constexpr (result_type == RequestOrResponse::BodyReadResult::Blob) {
JS::RootedString contentType(cx, JS_GetEmptyString(cx));
JS::RootedObject blob(cx, blob::Blob::create(cx, std::move(buf), len, contentType));

RootedObject headers(cx, RequestOrResponse::headers(cx, self));
if (!headers) {
return RejectPromiseWithPendingError(cx, result_promise);
}
auto content_type_key = host_api::HostString("Content-Type");
auto idx = Headers::lookup(cx, headers, content_type_key);
if (idx) {
auto *values = Headers::get_index(cx, headers, idx.value());
auto maybe_mime = extract_mime_type(std::get<1>(*values));
if (!maybe_mime.isErr()) {
auto mime_str = maybe_mime.unwrap().to_string();
JS::RootedString mime_js_str(cx, JS_NewStringCopyN(cx, mime_str.c_str(), mime_str.size()));
if (!mime_js_str) {
return RejectPromiseWithPendingError(cx, result_promise);
}
JS::RootedValue mime_val(cx, JS::StringValue(mime_js_str));
auto *normalized = blob::Blob::normalize_type(cx, mime_val);
if (!normalized) {
return RejectPromiseWithPendingError(cx, result_promise);
}
contentType = normalized;
}
}

JS::RootedObject blob(cx, blob::Blob::create(cx, std::move(buf), len, contentType));
if (!blob) {
return RejectPromiseWithPendingError(cx, result_promise);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"status": "PASS"
},
"Consume multipart/form-data headers case-insensitively": {
"status": "FAIL"
"status": "PASS"
}
}
Loading