| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 53cf53c commit 37dda19
14 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4948,6 +4948,20 @@ added: v3.0.0 | |||
| 4948 | 4948 | ||
| 4949 | 4949 | An alias for [`buffer.constants.MAX_STRING_LENGTH`][]. | |
| 4950 | 4950 | ||
| 4951 | + ### `buffer.resolveObjectURL(id)` | ||
| 4952 | + <!-- YAML | ||
| 4953 | + added: REPLACEME | ||
| 4954 | + --> | ||
| 4955 | + | ||
| 4956 | + > Stability: 1 - Experimental | ||
| 4957 | + | ||
| 4958 | + * `id` {string} A `'blob:nodedata:...` URL string returned by a prior call to | ||
| 4959 | + `URL.createObjectURL()`. | ||
| 4960 | + * Returns: {Blob} | ||
| 4961 | + | ||
| 4962 | + Resolves a `'blob:nodedata:...'` an associated {Blob} object registered using | ||
| 4963 | + a prior call to `URL.createObjectURL()`. | ||
| 4964 | + | ||
| 4951 | 4965 | ### `buffer.transcode(source, fromEnc, toEnc)` | |
| 4952 | 4966 | <!-- YAML | |
| 4953 | 4967 | added: v7.1.0 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -608,6 +608,53 @@ console.log(JSON.stringify(myURLs)); | |||
| 608 | 608 | // Prints ["https://www.example.com/","https://test.example.org/"] | |
| 609 | 609 | ``` | |
| 610 | 610 | ||
| 611 | + #### `URL.createObjectURL(blob)` | ||
| 612 | + <!-- YAML | ||
| 613 | + added: REPLACEME | ||
| 614 | + --> | ||
| 615 | + | ||
| 616 | + > Stability: 1 - Experimental | ||
| 617 | + | ||
| 618 | + * `blob` {Blob} | ||
| 619 | + * Returns: {string} | ||
| 620 | + | ||
| 621 | + Creates a `'blob:nodedata:...'` URL string that represents the given {Blob} | ||
| 622 | + object and can be used to retrieve the `Blob` later. | ||
| 623 | + | ||
| 624 | + ```js | ||
| 625 | + const { | ||
| 626 | + Blob, | ||
| 627 | + resolveObjectURL, | ||
| 628 | + } = require('buffer'); | ||
| 629 | + | ||
| 630 | + const blob = new Blob(['hello']); | ||
| 631 | + const id = URL.createObjectURL(blob); | ||
| 632 | + | ||
| 633 | + // later... | ||
| 634 | + | ||
| 635 | + const otherBlob = resolveObjectURL(id); | ||
| 636 | + console.log(otherBlob.size); | ||
| 637 | + ``` | ||
| 638 | + | ||
| 639 | + The data stored by the registered {Blob} will be retained in memory until | ||
| 640 | + `URL.revokeObjectURL()` is called to remove it. | ||
| 641 | + | ||
| 642 | + `Blob` objects are registered within the current thread. If using Worker | ||
| 643 | + Threads, `Blob` objects registered within one Worker will not be available | ||
| 644 | + to other workers or the main thread. | ||
| 645 | + | ||
| 646 | + #### `URL.revokeObjectURL(id)` | ||
| 647 | + <!-- YAML | ||
| 648 | + added: REPLACEME | ||
| 649 | + --> | ||
| 650 | + | ||
| 651 | + > Stability: 1 - Experimental | ||
| 652 | + | ||
| 653 | + * `id` {string} A `'blob:nodedata:...` URL string returned by a prior call to | ||
| 654 | + `URL.createObjectURL()`. | ||
| 655 | + | ||
| 656 | + Removes the stored {Blob} identified by the given ID. | ||
| 657 | + | ||
| 611 | 658 | ### Class: `URLSearchParams` | |
| 612 | 659 | <!-- YAML | |
| 613 | 660 | added: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -120,6 +120,7 @@ const { | |||
| 120 | 120 | ||
| 121 | 121 | const { | |
| 122 | 122 | Blob, | |
| 123 | + resolveObjectURL, | ||
| 123 | 124 | } = require('internal/blob'); | |
| 124 | 125 | ||
| 125 | 126 | FastBuffer.prototype.constructor = Buffer; | |
@@ -1239,6 +1240,7 @@ function atob(input) { | |||
| 1239 | 1240 | ||
| 1240 | 1241 | module.exports = { | |
| 1241 | 1242 | Blob, | |
| 1243 | + resolveObjectURL, | ||
| 1242 | 1244 | Buffer, | |
| 1243 | 1245 | SlowBuffer, | |
| 1244 | 1246 | transcode, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,10 +7,11 @@ const { | |||
| 7 | 7 | ObjectDefineProperty, | |
| 8 | 8 | PromiseResolve, | |
| 9 | 9 | PromiseReject, | |
| 10 | - PromisePrototypeFinally, | ||
| 10 | + SafePromisePrototypeFinally, | ||
| 11 | 11 | ReflectConstruct, | |
| 12 | 12 | RegExpPrototypeTest, | |
| 13 | 13 | StringPrototypeToLowerCase, | |
| 14 | + StringPrototypeSplit, | ||
| 14 | 15 | Symbol, | |
| 15 | 16 | SymbolIterator, | |
| 16 | 17 | SymbolToStringTag, | |
@@ -20,7 +21,8 @@ const { | |||
| 20 | 21 | const { | |
| 21 | 22 | createBlob: _createBlob, | |
| 22 | 23 | FixedSizeBlobCopyJob, | |
| 23 | - } = internalBinding('buffer'); | ||
| 24 | + getDataObject, | ||
| 25 | + } = internalBinding('blob'); | ||
| 24 | 26 | ||
| 25 | 27 | const { TextDecoder } = require('internal/encoding'); | |
| 26 | 28 | ||
@@ -57,26 +59,37 @@ const { | |||
| 57 | 59 | } = require('internal/validators'); | |
| 58 | 60 | ||
| 59 | 61 | const kHandle = Symbol('kHandle'); | |
| 62 | + const kState = Symbol('kState'); | ||
| 60 | 63 | const kType = Symbol('kType'); | |
| 61 | 64 | const kLength = Symbol('kLength'); | |
| 62 | 65 | const kArrayBufferPromise = Symbol('kArrayBufferPromise'); | |
| 63 | 66 | ||
| 67 | + const kMaxChunkSize = 65536; | ||
| 68 | + | ||
| 64 | 69 | const disallowedTypeCharacters = /[^\u{0020}-\u{007E}]/u; | |
| 65 | 70 | ||
| 66 | 71 | let Buffer; | |
| 67 | 72 | let ReadableStream; | |
| 73 | + let URL; | ||
| 74 | + | ||
| 75 | + | ||
| 76 | + // Yes, lazy loading is annoying but because of circular | ||
| 77 | + // references between the url, internal/blob, and buffer | ||
| 78 | + // modules, lazy loading here makes sure that things work. | ||
| 79 | + | ||
| 80 | + function lazyURL(id) { | ||
| 81 | + URL ??= require('internal/url').URL; | ||
| 82 | + return new URL(id); | ||
| 83 | + } | ||
| 68 | 84 | ||
| 69 | 85 | function lazyBuffer() { | |
| 70 | - if (Buffer === undefined) | ||
| 71 | - Buffer = require('buffer').Buffer; | ||
| 86 | + Buffer ??= require('buffer').Buffer; | ||
| 72 | 87 | return Buffer; | |
| 73 | 88 | } | |
| 74 | 89 | ||
| 75 | 90 | function lazyReadableStream(options) { | |
| 76 | - if (ReadableStream === undefined) { | ||
| 77 | - ReadableStream = | ||
| 78 | - require('internal/webstreams/readablestream').ReadableStream; | ||
| 79 | - } | ||
| 91 | + ReadableStream ??= | ||
| 92 | + require('internal/webstreams/readablestream').ReadableStream; | ||
| 80 | 93 | return new ReadableStream(options); | |
| 81 | 94 | } | |
| 82 | 95 | ||
@@ -232,9 +245,9 @@ class Blob { | |||
| 232 | 245 | return PromiseReject(new ERR_INVALID_THIS('Blob')); | |
| 233 | 246 | ||
| 234 | 247 | // If there's already a promise in flight for the content, | |
| 235 | - // reuse it, but only once. After the cached promise resolves | ||
| 236 | - // it will be cleared, allowing it to be garbage collected | ||
| 237 | - // as soon as possible. | ||
| 248 | + // reuse it, but only while it's in flight. After the cached | ||
| 249 | + // promise resolves it will be cleared, allowing it to be | ||
| 250 | + // garbage collected as soon as possible. | ||
| 238 | 251 | if (this[kArrayBufferPromise]) | |
| 239 | 252 | return this[kArrayBufferPromise]; | |
| 240 | 253 | ||
@@ -260,15 +273,14 @@ class Blob { | |||
| 260 | 273 | resolve(ab); | |
| 261 | 274 | }; | |
| 262 | 275 | this[kArrayBufferPromise] = | |
| 263 | - PromisePrototypeFinally( | ||
| 276 | + SafePromisePrototypeFinally( | ||
| 264 | 277 | promise, | |
| 265 | 278 | () => this[kArrayBufferPromise] = undefined); | |
| 266 | 279 | ||
| 267 | 280 | return this[kArrayBufferPromise]; | |
| 268 | 281 | } | |
| 269 | 282 | ||
| 270 | 283 | /** | |
| 271 | - * | ||
| 272 | 284 | * @returns {Promise<string>} | |
| 273 | 285 | */ | |
| 274 | 286 | async text() { | |
@@ -288,10 +300,20 @@ class Blob { | |||
| 288 | 300 | ||
| 289 | 301 | const self = this; | |
| 290 | 302 | return new lazyReadableStream({ | |
| 291 | - async start(controller) { | ||
| 292 | - const ab = await self.arrayBuffer(); | ||
| 293 | - controller.enqueue(new Uint8Array(ab)); | ||
| 294 | - controller.close(); | ||
| 303 | + async start() { | ||
| 304 | + this[kState] = await self.arrayBuffer(); | ||
| 305 | + }, | ||
| 306 | + | ||
| 307 | + pull(controller) { | ||
| 308 | + if (this[kState].byteLength <= kMaxChunkSize) { | ||
| 309 | + controller.enqueue(new Uint8Array(this[kState])); | ||
| 310 | + controller.close(); | ||
| 311 | + this[kState] = undefined; | ||
| 312 | + } else { | ||
| 313 | + const slice = this[kState].slice(0, kMaxChunkSize); | ||
| 314 | + this[kState] = this[kState].slice(kMaxChunkSize); | ||
| 315 | + controller.enqueue(new Uint8Array(slice)); | ||
| 316 | + } | ||
| 295 | 317 | } | |
| 296 | 318 | }); | |
| 297 | 319 | } | |
@@ -315,9 +337,47 @@ ObjectDefineProperty(Blob.prototype, SymbolToStringTag, { | |||
| 315 | 337 | value: 'Blob', | |
| 316 | 338 | }); | |
| 317 | 339 | ||
| 340 | + function resolveObjectURL(url) { | ||
| 341 | + url = `${url}`; | ||
| 342 | + try { | ||
| 343 | + const parsed = new lazyURL(url); | ||
| 344 | + | ||
| 345 | + const split = StringPrototypeSplit(parsed.pathname, ':'); | ||
| 346 | + | ||
| 347 | + if (split.length !== 2) | ||
| 348 | + return; | ||
| 349 | + | ||
| 350 | + const { | ||
| 351 | + 0: base, | ||
| 352 | + 1: id, | ||
| 353 | + } = split; | ||
| 354 | + | ||
| 355 | + if (base !== 'nodedata') | ||
| 356 | + return; | ||
| 357 | + | ||
| 358 | + const ret = getDataObject(id); | ||
| 359 | + | ||
| 360 | + if (ret === undefined) | ||
| 361 | + return; | ||
| 362 | + | ||
| 363 | + const { | ||
| 364 | + 0: handle, | ||
| 365 | + 1: length, | ||
| 366 | + 2: type, | ||
| 367 | + } = ret; | ||
| 368 | + | ||
| 369 | + if (handle !== undefined) | ||
| 370 | + return createBlob(handle, length, type); | ||
| 371 | + } catch { | ||
| 372 | + // If there's an error, it's ignored and nothing is returned | ||
| 373 | + } | ||
| 374 | + } | ||
| 375 | + | ||
| 318 | 376 | module.exports = { | |
| 319 | 377 | Blob, | |
| 320 | 378 | ClonedBlob, | |
| 321 | 379 | createBlob, | |
| 322 | 380 | isBlob, | |
| 381 | + kHandle, | ||
| 382 | + resolveObjectURL, | ||
| 323 | 383 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,17 +42,20 @@ const { | |||
| 42 | 42 | ||
| 43 | 43 | const { getConstructorOf, removeColors } = require('internal/util'); | |
| 44 | 44 | 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'); | ||
| 56 | 59 | const { | |
| 57 | 60 | CHAR_AMPERSAND, | |
| 58 | 61 | CHAR_BACKWARD_SLASH, | |
@@ -100,6 +103,11 @@ const { | |||
| 100 | 103 | kSchemeStart | |
| 101 | 104 | } = internalBinding('url'); | |
| 102 | 105 | ||
| 106 | + const { | ||
| 107 | + storeDataObject, | ||
| 108 | + revokeDataObject, | ||
| 109 | + } = internalBinding('blob'); | ||
| 110 | + | ||
| 103 | 111 | const context = Symbol('context'); | |
| 104 | 112 | const cannotBeBase = Symbol('cannot-be-base'); | |
| 105 | 113 | const cannotHaveUsernamePasswordPort = | |
@@ -108,6 +116,24 @@ const special = Symbol('special'); | |||
| 108 | 116 | const searchParams = Symbol('query'); | |
| 109 | 117 | const kFormat = Symbol('format'); | |
| 110 | 118 | ||
| 119 | + let blob; | ||
| 120 | + let cryptoRandom; | ||
| 121 | + | ||
| 122 | + function lazyBlob() { | ||
| 123 | + blob ??= require('internal/blob'); | ||
| 124 | + return blob; | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + function lazyCryptoRandom() { | ||
| 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 | + } | ||
| 134 | + return cryptoRandom; | ||
| 135 | + } | ||
| 136 | + | ||
| 111 | 137 | // https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object | |
| 112 | 138 | const IteratorPrototype = ObjectGetPrototypeOf( | |
| 113 | 139 | ObjectGetPrototypeOf([][SymbolIterator]()) | |
@@ -930,6 +956,37 @@ class URL { | |||
| 930 | 956 | toJSON() { | |
| 931 | 957 | return this[kFormat]({}); | |
| 932 | 958 | } | |
| 959 | + | ||
| 960 | + 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. | ||
| 968 | + const blob = lazyBlob(); | ||
| 969 | + if (!blob.isBlob(obj)) | ||
| 970 | + throw new ERR_INVALID_ARG_TYPE('obj', 'Blob', obj); | ||
| 971 | + | ||
| 972 | + const id = cryptoRandom.randomUUID(); | ||
| 973 | + | ||
| 974 | + storeDataObject(id, obj[blob.kHandle], obj.size, obj.type); | ||
| 975 | + | ||
| 976 | + return `blob:nodedata:${id}`; | ||
| 977 | + } | ||
| 978 | + | ||
| 979 | + static revokeObjectURL(url) { | ||
| 980 | + url = `${url}`; | ||
| 981 | + try { | ||
| 982 | + const parsed = new URL(url); | ||
| 983 | + const split = StringPrototypeSplit(parsed.pathname, ':'); | ||
| 984 | + if (split.length === 2) | ||
| 985 | + revokeDataObject(split[1]); | ||
| 986 | + } catch { | ||
| 987 | + // If there's an error, it's ignored. | ||
| 988 | + } | ||
| 989 | + } | ||
| 933 | 990 | } | |
| 934 | 991 | ||
| 935 | 992 | ObjectDefineProperties(URL.prototype, { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -40,6 +40,7 @@ | |||
| 40 | 40 | // __attribute__((constructor)) like mechanism in GCC. | |
| 41 | 41 | #define NODE_BUILTIN_STANDARD_MODULES(V) \ | |
| 42 | 42 | V(async_wrap) \ | |
| 43 | + V(blob) \ | ||
| 43 | 44 | V(block_list) \ | |
| 44 | 45 | V(buffer) \ | |
| 45 | 46 | V(cares_wrap) \ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments