Skip to content

Commit 672d46f

Browse files
committed
fixup! url,buffer: implement URL.createObjectURL
1 parent e91203d commit 672d46f

File tree

15 files changed

+228
-114
lines changed

15 files changed

+228
-114
lines changed

doc/api/buffer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4959,11 +4959,11 @@ added: REPLACEME
49594959

49604960
> Stability: 1 - Experimental
49614961
4962-
* `id` {string} A `'blob:node:...` URL string returned by a prior call to
4962+
* `id` {string} A `'blob:nodedata:...` URL string returned by a prior call to
49634963
`URL.createObjectURL()`.
49644964
* Returns: {Blob}
49654965

4966-
Resolves a `'blob:node:...'` an associated {Blob} object registered using
4966+
Resolves a `'blob:nodedata:...'` an associated {Blob} object registered using
49674967
a prior call to `URL.createObjectURL()`.
49684968

49694969
### `buffer.transcode(source, fromEnc, toEnc)`

doc/api/url.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,8 @@ added: REPLACEME
618618
* `blob` {Blob}
619619
* Returns: {string}
620620

621-
Creates a `'blob:node:...'` URL string that represents the given {Blob} object
622-
and can be used to retrieve the `Blob` later.
621+
Creates a `'blob:nodedata:...'` URL string that represents the given {Blob}
622+
object and can be used to retrieve the `Blob` later.
623623

624624
```js
625625
const {
@@ -641,8 +641,7 @@ The data stored by the registered {Blob} will be retained in memory until
641641

642642
`Blob` objects are registered within the current thread. If using Worker
643643
Threads, `Blob` objects registered within one Worker will not be available
644-
to other workers or the main thread. The `threadId` of the owning thread
645-
is encoded within the generated object URL.
644+
to other workers or the main thread.
646645

647646
#### `URL.revokeObjectURL(id)`
648647
<!-- YAML
@@ -651,7 +650,7 @@ added: REPLACEME
651650

652651
> Stability: 1 - Experimental
653652
654-
* `id` {string} A `'blob:node:...` URL string returned by a prior call to
653+
* `id` {string} A `'blob:nodedata:...` URL string returned by a prior call to
655654
`URL.createObjectURL()`.
656655

657656
Removes the stored {Blob} identified by the given ID.

lib/internal/blob.js

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ const {
77
ObjectDefineProperty,
88
PromiseResolve,
99
PromiseReject,
10-
PromisePrototypeFinally,
10+
SafePromisePrototypeFinally,
1111
ReflectConstruct,
1212
RegExpPrototypeTest,
1313
StringPrototypeToLowerCase,
14+
StringPrototypeSplit,
1415
Symbol,
1516
SymbolIterator,
1617
SymbolToStringTag,
@@ -21,7 +22,7 @@ const {
2122
createBlob: _createBlob,
2223
FixedSizeBlobCopyJob,
2324
getDataObject,
24-
} = internalBinding('buffer');
25+
} = internalBinding('blob');
2526

2627
const { TextDecoder } = require('internal/encoding');
2728

@@ -66,35 +67,29 @@ const disallowedTypeCharacters = /[^\u{0020}-\u{007E}]/u;
6667

6768
let Buffer;
6869
let ReadableStream;
69-
let threadId;
7070
let URL;
7171

72+
73+
// Yes, lazy loading is annoying but because of circular
74+
// references between the url, internal/blob, and buffer
75+
// modules, lazy loading here makes sure that things work.
76+
7277
function lazyURL(id) {
73-
if (URL === undefined)
74-
URL = require('url').URL;
78+
URL ??= require('url').URL;
7579
return new URL(id);
7680
}
7781

7882
function lazyBuffer() {
79-
if (Buffer === undefined)
80-
Buffer = require('buffer').Buffer;
83+
Buffer ??= require('buffer').Buffer;
8184
return Buffer;
8285
}
8386

8487
function lazyReadableStream(options) {
85-
if (ReadableStream === undefined) {
86-
ReadableStream =
87-
require('internal/webstreams/readablestream').ReadableStream;
88-
}
88+
ReadableStream ??=
89+
require('internal/webstreams/readablestream').ReadableStream;
8990
return new ReadableStream(options);
9091
}
9192

92-
function lazyThreadId() {
93-
if (threadId === undefined)
94-
threadId = require('worker_threads').threadId;
95-
return threadId;
96-
}
97-
9893
function isBlob(object) {
9994
return object?.[kHandle] !== undefined;
10095
}
@@ -275,15 +270,14 @@ class Blob {
275270
resolve(ab);
276271
};
277272
this[kArrayBufferPromise] =
278-
PromisePrototypeFinally(
273+
SafePromisePrototypeFinally(
279274
promise,
280275
() => this[kArrayBufferPromise] = undefined);
281276

282277
return this[kArrayBufferPromise];
283278
}
284279

285280
/**
286-
*
287281
* @returns {Promise<string>}
288282
*/
289283
async text() {
@@ -334,13 +328,18 @@ function resolveObjectURL(url) {
334328
url = `${url}`;
335329
try {
336330
const parsed = new lazyURL(url);
331+
332+
const split = StringPrototypeSplit(parsed.pathname, ':');
333+
334+
if (split.length !== 2)
335+
return;
336+
337337
const {
338338
0: base,
339-
1: threadId,
340-
2: id,
341-
} = parsed.pathname.split(':');
339+
1: id,
340+
} = split;
342341

343-
if (base !== 'node' || +threadId !== lazyThreadId())
342+
if (base !== 'nodedata')
344343
return;
345344

346345
const ret = getDataObject(id);

lib/internal/url.js

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,20 @@ const {
4242

4343
const { getConstructorOf, removeColors } = require('internal/util');
4444
const {
45-
ERR_ARG_NOT_ITERABLE,
46-
ERR_INVALID_ARG_TYPE,
47-
ERR_INVALID_ARG_VALUE,
48-
ERR_INVALID_FILE_URL_HOST,
49-
ERR_INVALID_FILE_URL_PATH,
50-
ERR_INVALID_THIS,
51-
ERR_INVALID_TUPLE,
52-
ERR_INVALID_URL,
53-
ERR_INVALID_URL_SCHEME,
54-
ERR_MISSING_ARGS
55-
} = require('internal/errors').codes;
45+
codes: {
46+
ERR_ARG_NOT_ITERABLE,
47+
ERR_INVALID_ARG_TYPE,
48+
ERR_INVALID_ARG_VALUE,
49+
ERR_INVALID_FILE_URL_HOST,
50+
ERR_INVALID_FILE_URL_PATH,
51+
ERR_INVALID_THIS,
52+
ERR_INVALID_TUPLE,
53+
ERR_INVALID_URL,
54+
ERR_INVALID_URL_SCHEME,
55+
ERR_MISSING_ARGS,
56+
ERR_NO_CRYPTO,
57+
},
58+
} = require('internal/errors');
5659
const {
5760
CHAR_AMPERSAND,
5861
CHAR_BACKWARD_SLASH,
@@ -103,7 +106,7 @@ const {
103106
const {
104107
storeDataObject,
105108
revokeDataObject,
106-
} = internalBinding('buffer');
109+
} = internalBinding('blob');
107110

108111
const context = Symbol('context');
109112
const cannotBeBase = Symbol('cannot-be-base');
@@ -115,26 +118,22 @@ const kFormat = Symbol('format');
115118

116119
let blob;
117120
let cryptoRandom;
118-
let threadId;
119121

120122
function lazyBlob() {
121-
if (blob === undefined)
122-
blob = require('internal/blob');
123+
blob ??= require('internal/blob');
123124
return blob;
124125
}
125126

126127
function lazyCryptoRandom() {
127-
if (cryptoRandom === undefined)
128-
cryptoRandom = require('internal/crypto/random');
128+
try {
129+
cryptoRandom ??= require('internal/crypto/random');
130+
} catch {
131+
// If Node.js built without crypto support, we'll fall
132+
// through here and handle it later.
133+
}
129134
return cryptoRandom;
130135
}
131136

132-
function lazyThreadId() {
133-
if (threadId === undefined)
134-
threadId = require('worker_threads').threadId;
135-
return threadId;
136-
}
137-
138137
// https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object
139138
const IteratorPrototype = ObjectGetPrototypeOf(
140139
ObjectGetPrototypeOf([][SymbolIterator]())
@@ -959,22 +958,29 @@ class URL {
959958
}
960959

961960
static createObjectURL(obj) {
961+
const cryptoRandom = lazyCryptoRandom();
962+
if (cryptoRandom === undefined)
963+
throw new ERR_NO_CRYPTO();
964+
965+
// Yes, lazy loading is annoying but because of circular
966+
// references between the url, internal/blob, and buffer
967+
// modules, lazy loading here makes sure that things work.
962968
const blob = lazyBlob();
963969
if (!blob.isBlob(obj))
964970
throw new ERR_INVALID_ARG_TYPE('obj', 'Blob', obj);
965971

966-
const id = lazyCryptoRandom().randomUUID();
972+
const id = cryptoRandom.randomUUID();
967973

968974
storeDataObject(id, obj[blob.kHandle], obj.size, obj.type);
969975

970-
return `blob:node:${lazyThreadId()}:${id}`;
976+
return `blob:nodedata:${id}`;
971977
}
972978

973979
static revokeObjectURL(url) {
974980
url = `${url}`;
975981
try {
976982
const parsed = new URL(url);
977-
const { 2: id } = parsed.pathname.split(':');
983+
const { 1: id } = StringPrototypeSplit(parsed.pathname, ':');
978984
if (id !== undefined)
979985
revokeDataObject(id);
980986
} catch {

src/env-inl.h

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,25 +1182,6 @@ void Environment::set_process_exit_handler(
11821182
process_exit_handler_ = std::move(handler);
11831183
}
11841184

1185-
void Environment::store_data_object(
1186-
const std::string& uuid,
1187-
const Environment::StoredDataObject& data_object) {
1188-
data_objects[uuid] = data_object;
1189-
}
1190-
1191-
void Environment::erase_data_object(const std::string& uuid) {
1192-
data_objects.erase(uuid);
1193-
}
1194-
1195-
Environment::StoredDataObject Environment::get_data_object(
1196-
const std::string& uuid) {
1197-
auto item = data_objects.find(uuid);
1198-
if (item == data_objects.end()) {
1199-
return Environment::StoredDataObject {};
1200-
}
1201-
return item->second;
1202-
}
1203-
12041185
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
12051186
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
12061187
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)

src/env.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,6 @@ void Environment::CleanupHandles() {
644644
!handle_wrap_queue_.IsEmpty()) {
645645
uv_run(event_loop(), UV_RUN_ONCE);
646646
}
647-
648-
data_objects.clear();
649647
}
650648

651649
void Environment::StartProfilerIdleNotifier() {

src/env.h

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,20 +1389,6 @@ class Environment : public MemoryRetainer {
13891389

13901390
inline int32_t stack_trace_limit() const { return 10; }
13911391

1392-
struct StoredDataObject {
1393-
BaseObjectPtr<BaseObject> data_object;
1394-
size_t length;
1395-
std::string type;
1396-
};
1397-
1398-
inline void store_data_object(
1399-
const std::string& uuid,
1400-
const StoredDataObject& object);
1401-
1402-
inline void erase_data_object(const std::string& uuid);
1403-
1404-
inline StoredDataObject get_data_object(const std::string& uuid);
1405-
14061392
#if HAVE_INSPECTOR
14071393
void set_coverage_connection(
14081394
std::unique_ptr<profiler::V8CoverageConnection> connection);
@@ -1625,9 +1611,6 @@ class Environment : public MemoryRetainer {
16251611
// a given pointer.
16261612
std::unordered_map<char*, std::unique_ptr<v8::BackingStore>>
16271613
released_allocated_buffers_;
1628-
1629-
// Maintains stored Blobs used with the URL.createObjectURL() API
1630-
std::unordered_map<std::string, StoredDataObject> data_objects;
16311614
};
16321615

16331616
} // namespace node

src/node_binding.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
// __attribute__((constructor)) like mechanism in GCC.
4141
#define NODE_BUILTIN_STANDARD_MODULES(V) \
4242
V(async_wrap) \
43+
V(blob) \
4344
V(block_list) \
4445
V(buffer) \
4546
V(cares_wrap) \

0 commit comments

Comments
 (0)