Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement URL.createObjectURL and URL.revokeObjectURL methods #187

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
46 changes: 27 additions & 19 deletions builtins/web/blob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,18 @@ namespace blob {

using js::Vector;

#define DEFINE_BLOB_METHOD(name) \
bool Blob::name(JSContext *cx, unsigned argc, JS::Value *vp) { \
METHOD_HEADER(0) \
return name(cx, self, args.rval()); \
}

#define DEFINE_BLOB_METHOD_W_ARGS(name) \
bool Blob::name(JSContext *cx, unsigned argc, JS::Value *vp) { \
METHOD_HEADER(0) \
return name(cx, self, args, args.rval()); \
}

const JSFunctionSpec Blob::static_methods[] = {
JS_FS_END,
};
Expand Down Expand Up @@ -269,15 +281,19 @@ JSObject *Blob::data_to_owned_array_buffer(JSContext *cx, HandleObject self, siz
return array_buffer;
}

bool Blob::arrayBuffer(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)
DEFINE_BLOB_METHOD(arrayBuffer)
DEFINE_BLOB_METHOD(bytes)
DEFINE_BLOB_METHOD(stream)
DEFINE_BLOB_METHOD(text)
DEFINE_BLOB_METHOD_W_ARGS(slice)

bool Blob::arrayBuffer(JSContext *cx, HandleObject self, MutableHandleValue rval) {
JS::RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr));
if (!promise) {
return false;
}

args.rval().setObject(*promise);
rval.setObject(*promise);

auto buffer = data_to_owned_array_buffer(cx, self);
if (!buffer) {
Expand All @@ -291,15 +307,13 @@ bool Blob::arrayBuffer(JSContext *cx, unsigned argc, JS::Value *vp) {
return true;
}

bool Blob::bytes(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)

bool Blob::bytes(JSContext *cx, HandleObject self, MutableHandleValue rval) {
JS::RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr));
if (!promise) {
return false;
}

args.rval().setObject(*promise);
rval.setObject(*promise);

JS::RootedObject buffer(cx, data_to_owned_array_buffer(cx, self));
if (!buffer) {
Expand All @@ -319,9 +333,7 @@ bool Blob::bytes(JSContext *cx, unsigned argc, JS::Value *vp) {
return true;
}

bool Blob::slice(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)

bool Blob::slice(JSContext *cx, HandleObject self, const CallArgs &args, MutableHandleValue rval) {
auto src = Blob::blob(self);
int64_t size = src->length();
int64_t start = 0;
Expand Down Expand Up @@ -366,13 +378,11 @@ bool Blob::slice(JSContext *cx, unsigned argc, JS::Value *vp) {
return false;
}

args.rval().setObject(*new_blob);
rval.setObject(*new_blob);
return true;
}

bool Blob::stream(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)

bool Blob::stream(JSContext *cx, HandleObject self, MutableHandleValue rval) {
auto native_stream = streams::NativeStreamSource::create(cx, self, JS::UndefinedHandleValue,
stream_pull, stream_cancel);

Expand All @@ -394,19 +404,17 @@ bool Blob::stream(JSContext *cx, unsigned argc, JS::Value *vp) {
return false;
}

args.rval().setObject(*stream);
rval.setObject(*stream);
return true;
}

bool Blob::text(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)

bool Blob::text(JSContext *cx, HandleObject self, MutableHandleValue rval) {
JS::RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr));
if (!promise) {
return false;
}

args.rval().setObject(*promise);
rval.setObject(*promise);

auto src = Blob::blob(self);
auto encoding = const_cast<jsencoding::Encoding *>(jsencoding::encoding_for_label_no_replacement(
Expand Down
9 changes: 8 additions & 1 deletion builtins/web/blob.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,19 @@ class Blob : public TraceableBuiltinImpl<Blob> {
static const JSPropertySpec properties[];

static constexpr unsigned ctor_length = 0;
enum Slots { Data, Type, Endings, Readers, Count };
enum Slots { Data, Type, Endings, Readers, Reserved2, Reserved1, Count };
enum LineEndings { Transparent, Native };

using HeapObj = Heap<JSObject *>;
using ByteBuffer = js::Vector<uint8_t, 0, js::SystemAllocPolicy>;
using ReadersMap = JS::GCHashMap<HeapObj, BlobReader, js::StableCellHasher<HeapObj>, js::SystemAllocPolicy>;

static bool arrayBuffer(JSContext *cx, HandleObject self, MutableHandleValue rval);
static bool bytes(JSContext *cx, HandleObject self, MutableHandleValue rval);
static bool stream(JSContext *cx, HandleObject self, MutableHandleValue rval);
static bool text(JSContext *cx, HandleObject self, MutableHandleValue rval);
static bool slice(JSContext *cx, HandleObject self, const CallArgs &args, MutableHandleValue rval);

static ReadersMap *readers(JSObject *self);
static ByteBuffer *blob(JSObject *self);
static size_t blob_size(JSObject *self);
Expand All @@ -75,6 +81,7 @@ class Blob : public TraceableBuiltinImpl<Blob> {
static JSObject *data_to_owned_array_buffer(JSContext *cx, HandleObject self);
static JSObject *data_to_owned_array_buffer(JSContext *cx, HandleObject self, size_t offset,
size_t size, size_t *bytes_read);

static JSObject *create(JSContext *cx, UniqueChars data, size_t data_len, HandleString type);

static bool init_class(JSContext *cx, HandleObject global);
Expand Down
1 change: 1 addition & 0 deletions builtins/web/crypto/crypto-algorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,7 @@ JSObject *CryptoAlgorithmRSASSA_PKCS1_v1_5_Import::importKey(JSContext *cx, Cryp
case CryptoKeyFormat::Spki:
case CryptoKeyFormat::Pkcs8: {
// TODO: Add implementations for these
[[fallthrough]];
}
default: {
DOMException::raise(cx, "Supplied format is not supported", "DataError");
Expand Down
116 changes: 7 additions & 109 deletions builtins/web/crypto/crypto.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
// TODO: remove these once the warnings are fixed
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Winvalid-offsetof"
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"
#include "js/experimental/TypedData.h" // used in "js/Conversions.h"
#pragma clang diagnostic pop

#include "../dom-exception.h"
#include "crypto.h"
#include "host_api.h"
#include "subtle-crypto.h"
#include "uuid.h"

bool is_int_typed_array(JSObject *obj) {
return JS_IsInt8Array(obj) || JS_IsUint8Array(obj) || JS_IsInt16Array(obj) ||
Expand Down Expand Up @@ -66,114 +60,18 @@ bool Crypto::get_random_values(JSContext *cx, unsigned argc, JS::Value *vp) {
return true;
}

/*
FROM RFC 4122
The formal definition of the UUID string representation is
provided by the following ABNF [7]:

UUID = time-low "-" time-mid "-"
time-high-and-version "-"
clock-seq-and-reserved
clock-seq-low "-" node
time-low = 4hexOctet
time-mid = 2hexOctet
time-high-and-version = 2hexOctet
clock-seq-and-reserved = hexOctet
clock-seq-low = hexOctet
node = 6hexOctet
hexOctet = hexDigit hexDigit
hexDigit =
"0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" /
"a" / "b" / "c" / "d" / "e" / "f" /
"A" / "B" / "C" / "D" / "E" / "F"
*/
struct UUID {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_high_and_version;
uint8_t clock_seq_and_reserved;
uint8_t clock_seq_low;
uint8_t node[6];
};

/*
FROM RFC 4122
4.1.3. Version

The version number is in the most significant 4 bits of the time
stamp (bits 4 through 7 of the time_hi_and_version field).

The following table lists the currently-defined versions for this
UUID variant.

Msb0 Msb1 Msb2 Msb3 Version Description

0 0 0 1 1 The time-based version
specified in this document.

0 0 1 0 2 DCE Security version, with
embedded POSIX UIDs.

0 0 1 1 3 The name-based version
specified in this document
that uses MD5 hashing.

0 1 0 0 4 The randomly or pseudo-
randomly generated version
specified in this document.

0 1 0 1 5 The name-based version
specified in this document
that uses SHA-1 hashing.
*/
/*
FROM RFC 4122
The version 4 UUID is meant for generating UUIDs from truly-random or
pseudo-random numbers.

The algorithm is as follows:

Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one,
respectively.

Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the
4-bit version number from Section 4.1.3.

Set all the other bits to randomly (or pseudo-randomly) chosen values.
*/
bool Crypto::random_uuid(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)
UUID id;

{
auto res = host_api::Random::get_bytes(sizeof(id));
if (auto *err = res.to_err()) {
HANDLE_ERROR(cx, *err);
return false;
}

auto bytes = std::move(res.unwrap());
std::copy_n(bytes.begin(), sizeof(id), reinterpret_cast<uint8_t *>(&id));
auto maybe_uuid = random_uuid_v4(cx);
if (!maybe_uuid.has_value()) {
return false;
}

// Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and
// one, respectively.
id.clock_seq_and_reserved &= 0x3f;
id.clock_seq_and_reserved |= 0x80;
// Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the
// 4-bit version number from Section 4.1.3.
id.time_high_and_version &= 0x0fff;
id.time_high_and_version |= 0x4000;

const char *fmt = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
char buf[80];
std::snprintf(buf, sizeof(buf) - 1, fmt, id.time_low, (uint32_t)id.time_mid,
(uint32_t)id.time_high_and_version, (uint32_t)id.clock_seq_and_reserved,
(uint32_t)id.clock_seq_low, (uint32_t)id.node[0], (uint32_t)id.node[1],
(uint32_t)id.node[2], (uint32_t)id.node[3], (uint32_t)id.node[4],
(uint32_t)id.node[5]);
JS::RootedString str(cx, JS_NewStringCopyN(cx, buf, 36));
auto uuid = maybe_uuid.value();
MOZ_ASSERT(uuid.size() == 36);

JS::RootedString str(cx, JS_NewStringCopyN(cx, uuid.data(), uuid.size()));
if (!str) {
return false;
}
Expand Down
100 changes: 100 additions & 0 deletions builtins/web/crypto/uuid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include "uuid.h"
#include "host_api.h"

#include <fmt/core.h>

namespace builtins {
namespace web {
namespace crypto {

// FROM RFC 4122
// The formal definition of the UUID string representation is
// provided by the following ABNF [7]:
//
// UUID = time-low "-" time-mid "-"
// time-high-and-version "-"
// clock-seq-and-reserved
// clock-seq-low "-" node
// time-low = 4hexOctet
// time-mid = 2hexOctet
// time-high-and-version = 2hexOctet
// clock-seq-and-reserved = hexOctet
// clock-seq-low = hexOctet
// node = 6hexOctet
// hexOctet = hexDigit hexDigit
// hexDigit =
// "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" /
// "a" / "b" / "c" / "d" / "e" / "f" /
// "A" / "B" / "C" / "D" / "E" / "F"
struct UUID {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_high_and_version;
uint8_t clock_seq_and_reserved;
uint8_t clock_seq_low;
uint8_t node[6];
};

// FROM RFC 4122
// 4.1.3. Version
//
// The version number is in the most significant 4 bits of the time
// stamp (bits 4 through 7 of the time_hi_and_version field).
//
// The following table lists the currently-defined versions for this
// UUID variant.
//
// Msb0 Msb1 Msb2 Msb3 Version Description
//
// 0 0 0 1 1 The time-based version
// specified in this document.
//
// 0 0 1 0 2 DCE Security version, with
// embedded POSIX UIDs.
//
// 0 0 1 1 3 The name-based version
// specified in this document
// that uses MD5 hashing.
//
// 0 1 0 0 4 The randomly or pseudo-
// randomly generated version
// specified in this document.
//
// 0 1 0 1 5 The name-based version
// specified in this document
// that uses SHA-1 hashing.
std::optional<std::string> random_uuid_v4(JSContext *cx) {
UUID id;

{
auto res = host_api::Random::get_bytes(sizeof(id));
if (auto *err = res.to_err()) {
HANDLE_ERROR(cx, *err);
return std::nullopt;
}

auto bytes = std::move(res.unwrap());
std::copy_n(bytes.begin(), sizeof(id), reinterpret_cast<uint8_t *>(&id));
}

// Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and
// one, respectively.
id.clock_seq_and_reserved &= 0x3f;
id.clock_seq_and_reserved |= 0x80;
// Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the
// 4-bit version number from Section 4.1.3.
id.time_high_and_version &= 0x0fff;
id.time_high_and_version |= 0x4000;

return fmt::format("{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
id.time_low,
id.time_mid,
id.time_high_and_version,
id.clock_seq_and_reserved, id.clock_seq_low,
id.node[0], id.node[1], id.node[2],
id.node[3], id.node[4], id.node[5]);
}

} // namespace uuid
} // namespace web
} // namespace builtins
Loading
Loading