123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- /**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @flow strict-local
- * @format
- */
- 'use strict';
- const Blob = require('./Blob');
- const BlobRegistry = require('./BlobRegistry');
- import type {BlobData, BlobOptions, BlobCollector} from './BlobTypes';
- import NativeBlobModule from './NativeBlobModule';
- import invariant from 'invariant';
- /*eslint-disable no-bitwise */
- /*eslint-disable eqeqeq */
- /**
- * Based on the rfc4122-compliant solution posted at
- * http://stackoverflow.com/questions/105034
- */
- function uuidv4(): string {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
- const r = (Math.random() * 16) | 0,
- v = c == 'x' ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- });
- }
- // **Temporary workaround**
- // TODO(#24654): Use turbomodules for the Blob module.
- // Blob collector is a jsi::HostObject that is used by native to know
- // when the a Blob instance is deallocated. This allows to free the
- // underlying native resources. This is a hack to workaround the fact
- // that the current bridge infra doesn't allow to track js objects
- // deallocation. Ideally the whole Blob object should be a jsi::HostObject.
- function createBlobCollector(blobId: string): BlobCollector | null {
- if (global.__blobCollectorProvider == null) {
- return null;
- } else {
- return global.__blobCollectorProvider(blobId);
- }
- }
- /**
- * Module to manage blobs. Wrapper around the native blob module.
- */
- class BlobManager {
- /**
- * If the native blob module is available.
- */
- static isAvailable: boolean = !!NativeBlobModule;
- /**
- * Create blob from existing array of blobs.
- */
- static createFromParts(
- parts: Array<Blob | string>,
- options?: BlobOptions,
- ): Blob {
- invariant(NativeBlobModule, 'NativeBlobModule is available.');
- const blobId = uuidv4();
- const items = parts.map(part => {
- if (
- part instanceof ArrayBuffer ||
- (global.ArrayBufferView && part instanceof global.ArrayBufferView)
- ) {
- throw new Error(
- "Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported",
- );
- }
- if (part instanceof Blob) {
- return {
- data: part.data,
- type: 'blob',
- };
- } else {
- return {
- data: String(part),
- type: 'string',
- };
- }
- });
- const size = items.reduce((acc, curr) => {
- if (curr.type === 'string') {
- return acc + global.unescape(encodeURI(curr.data)).length;
- } else {
- return acc + curr.data.size;
- }
- }, 0);
- NativeBlobModule.createFromParts(items, blobId);
- return BlobManager.createFromOptions({
- blobId,
- offset: 0,
- size,
- type: options ? options.type : '',
- lastModified: options ? options.lastModified : Date.now(),
- });
- }
- /**
- * Create blob instance from blob data from native.
- * Used internally by modules like XHR, WebSocket, etc.
- */
- static createFromOptions(options: BlobData): Blob {
- BlobRegistry.register(options.blobId);
- return Object.assign(Object.create(Blob.prototype), {
- data:
- // Reuse the collector instance when creating from an existing blob.
- // This will make sure that the underlying resource is only deallocated
- // when all blobs that refer to it are deallocated.
- options.__collector == null
- ? {
- ...options,
- __collector: createBlobCollector(options.blobId),
- }
- : options,
- });
- }
- /**
- * Deallocate resources for a blob.
- */
- static release(blobId: string): void {
- invariant(NativeBlobModule, 'NativeBlobModule is available.');
- BlobRegistry.unregister(blobId);
- if (BlobRegistry.has(blobId)) {
- return;
- }
- NativeBlobModule.release(blobId);
- }
- /**
- * Inject the blob content handler in the networking module to support blob
- * requests and responses.
- */
- static addNetworkingHandler(): void {
- invariant(NativeBlobModule, 'NativeBlobModule is available.');
- NativeBlobModule.addNetworkingHandler();
- }
- /**
- * Indicate the websocket should return a blob for incoming binary
- * messages.
- */
- static addWebSocketHandler(socketId: number): void {
- invariant(NativeBlobModule, 'NativeBlobModule is available.');
- NativeBlobModule.addWebSocketHandler(socketId);
- }
- /**
- * Indicate the websocket should no longer return a blob for incoming
- * binary messages.
- */
- static removeWebSocketHandler(socketId: number): void {
- invariant(NativeBlobModule, 'NativeBlobModule is available.');
- NativeBlobModule.removeWebSocketHandler(socketId);
- }
- /**
- * Send a blob message to a websocket.
- */
- static sendOverSocket(blob: Blob, socketId: number): void {
- invariant(NativeBlobModule, 'NativeBlobModule is available.');
- NativeBlobModule.sendOverSocket(blob.data, socketId);
- }
- }
- module.exports = BlobManager;
|