mirror of
https://github.com/status-im/react-native.git
synced 2025-01-15 03:56:03 +00:00
ed903099b4
Summary: This is the first PR from a series of PRs grabbou and me will make to add blob support to React Native. The next PR will include blob support for XMLHttpRequest. I'd like to get this merged with minimal changes to preserve the attribution. My next PR can contain bigger changes. Blobs are used to transfer binary data between server and client. Currently React Native lacks a way to deal with binary data. The only thing that comes close is uploading files through a URI. Current workarounds to transfer binary data includes encoding and decoding them to base64 and and transferring them as string, which is not ideal, since it increases the payload size and the whole payload needs to be sent via the bridge every time changes are made. The PR adds a way to deal with blobs via a new native module. The blob is constructed on the native side and the data never needs to pass through the bridge. Currently the only way to create a blob is to receive a blob from the server via websocket. The PR is largely a direct port of https://github.com/silklabs/silk/tree/master/react-native-blobs by philikon into RN (with changes to integrate with RN), and attributed as such. > **Note:** This is a breaking change for all people running iOS without CocoaPods. You will have to manually add `RCTBlob.xcodeproj` to your `Libraries` and then, add it to Build Phases. Just follow the process of manual linking. We'll also need to document this process in the release notes. Related discussion - https://github.com/facebook/react-native/issues/11103 - `Image` can't show image when `URL.createObjectURL` is used with large images on Android The websocket integration can be tested via a simple server, ```js const fs = require('fs'); const http = require('http'); const WebSocketServer = require('ws').Server; const wss = new WebSocketServer({ server: http.createServer().listen(7232), }); wss.on('connection', (ws) => { ws.on('message', (d) => { console.log(d); }); ws.send(fs.readFileSync('./some-file')); }); ``` Then on the client, ```js var ws = new WebSocket('ws://localhost:7232'); ws.binaryType = 'blob'; ws.onerror = (error) => { console.error(error); }; ws.onmessage = (e) => { console.log(e.data); ws.send(e.data); }; ``` cc brentvatne ide Closes https://github.com/facebook/react-native/pull/11417 Reviewed By: sahrens Differential Revision: D5188484 Pulled By: javache fbshipit-source-id: 6afcbc4d19aa7a27b0dc9d52701ba400e7d7e98f
223 lines
7.1 KiB
JavaScript
223 lines
7.1 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule InitializeCore
|
|
* @flow
|
|
*/
|
|
|
|
/* eslint-disable strict */
|
|
/* globals window: true */
|
|
|
|
/**
|
|
* Sets up global variables typical in most JavaScript environments.
|
|
*
|
|
* 1. Global timers (via `setTimeout` etc).
|
|
* 2. Global console object.
|
|
* 3. Hooks for printing stack traces with source maps.
|
|
*
|
|
* Leaves enough room in the environment for implementing your own:
|
|
*
|
|
* 1. Require system.
|
|
* 2. Bridged modules.
|
|
*
|
|
*/
|
|
'use strict';
|
|
|
|
if (global.GLOBAL === undefined) {
|
|
global.GLOBAL = global;
|
|
}
|
|
|
|
if (global.window === undefined) {
|
|
global.window = global;
|
|
}
|
|
|
|
const defineLazyObjectProperty = require('defineLazyObjectProperty');
|
|
|
|
/**
|
|
* Sets an object's property. If a property with the same name exists, this will
|
|
* replace it but maintain its descriptor configuration. The property will be
|
|
* replaced with a lazy getter.
|
|
*
|
|
* In DEV mode the original property value will be preserved as `original[PropertyName]`
|
|
* so that, if necessary, it can be restored. For example, if you want to route
|
|
* network requests through DevTools (to trace them):
|
|
*
|
|
* global.XMLHttpRequest = global.originalXMLHttpRequest;
|
|
*
|
|
* @see https://github.com/facebook/react-native/issues/934
|
|
*/
|
|
function defineLazyProperty<T>(
|
|
object: Object,
|
|
name: string,
|
|
getValue: () => T,
|
|
): void {
|
|
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
|
if (__DEV__ && descriptor) {
|
|
const backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
|
|
Object.defineProperty(object, backupName, {
|
|
...descriptor,
|
|
value: object[name],
|
|
});
|
|
}
|
|
|
|
const {enumerable, writable, configurable} = descriptor || {};
|
|
if (descriptor && !configurable) {
|
|
console.error('Failed to set polyfill. ' + name + ' is not configurable.');
|
|
return;
|
|
}
|
|
|
|
defineLazyObjectProperty(object, name, {
|
|
get: getValue,
|
|
enumerable: enumerable !== false,
|
|
writable: writable !== false,
|
|
});
|
|
}
|
|
|
|
function polyfillGlobal<T>(name: string, getValue: () => T): void {
|
|
defineLazyProperty(global, name, getValue);
|
|
}
|
|
|
|
// Set up process
|
|
global.process = global.process || {};
|
|
global.process.env = global.process.env || {};
|
|
if (!global.process.env.NODE_ENV) {
|
|
global.process.env.NODE_ENV = __DEV__ ? 'development' : 'production';
|
|
}
|
|
|
|
// Setup the Systrace profiling hooks if necessary
|
|
if (global.__RCTProfileIsProfiling) {
|
|
const Systrace = require('Systrace');
|
|
Systrace.setEnabled(true);
|
|
}
|
|
|
|
if (__DEV__ && global.performance === undefined) {
|
|
const Systrace = require('Systrace');
|
|
global.performance = Systrace.getUserTimingPolyfill();
|
|
}
|
|
|
|
// Set up console
|
|
const ExceptionsManager = require('ExceptionsManager');
|
|
ExceptionsManager.installConsoleErrorReporter();
|
|
|
|
// Set up error handler
|
|
if (!global.__fbDisableExceptionsManager) {
|
|
const handleError = (e, isFatal) => {
|
|
try {
|
|
ExceptionsManager.handleException(e, isFatal);
|
|
} catch (ee) {
|
|
/* eslint-disable no-console-disallow */
|
|
console.log('Failed to print error: ', ee.message);
|
|
/* eslint-enable no-console-disallow */
|
|
throw e;
|
|
}
|
|
};
|
|
|
|
const ErrorUtils = require('ErrorUtils');
|
|
ErrorUtils.setGlobalHandler(handleError);
|
|
}
|
|
|
|
// Set up collections
|
|
const _shouldPolyfillCollection = require('_shouldPolyfillES6Collection');
|
|
if (_shouldPolyfillCollection('Map')) {
|
|
polyfillGlobal('Map', () => require('Map'));
|
|
}
|
|
if (_shouldPolyfillCollection('Set')) {
|
|
polyfillGlobal('Set', () => require('Set'));
|
|
}
|
|
|
|
// Set up Promise
|
|
// The native Promise implementation throws the following error:
|
|
// ERROR: Event loop not supported.
|
|
polyfillGlobal('Promise', () => require('Promise'));
|
|
|
|
// Set up regenerator.
|
|
polyfillGlobal('regeneratorRuntime', () => {
|
|
// The require just sets up the global, so make sure when we first
|
|
// invoke it the global does not exist
|
|
delete global.regeneratorRuntime;
|
|
require('regenerator-runtime/runtime');
|
|
return global.regeneratorRuntime;
|
|
});
|
|
|
|
// Set up timers
|
|
const defineLazyTimer = name => {
|
|
polyfillGlobal(name, () => require('JSTimers')[name]);
|
|
};
|
|
defineLazyTimer('setTimeout');
|
|
defineLazyTimer('setInterval');
|
|
defineLazyTimer('setImmediate');
|
|
defineLazyTimer('clearTimeout');
|
|
defineLazyTimer('clearInterval');
|
|
defineLazyTimer('clearImmediate');
|
|
defineLazyTimer('requestAnimationFrame');
|
|
defineLazyTimer('cancelAnimationFrame');
|
|
defineLazyTimer('requestIdleCallback');
|
|
defineLazyTimer('cancelIdleCallback');
|
|
|
|
// Set up XHR
|
|
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
|
|
// let you fetch anything from the internet
|
|
polyfillGlobal('XMLHttpRequest', () => require('XMLHttpRequest'));
|
|
polyfillGlobal('FormData', () => require('FormData'));
|
|
|
|
polyfillGlobal('fetch', () => require('fetch').fetch);
|
|
polyfillGlobal('Headers', () => require('fetch').Headers);
|
|
polyfillGlobal('Request', () => require('fetch').Request);
|
|
polyfillGlobal('Response', () => require('fetch').Response);
|
|
polyfillGlobal('WebSocket', () => require('WebSocket'));
|
|
polyfillGlobal('Blob', () => require('Blob'));
|
|
polyfillGlobal('URL', () => require('URL'));
|
|
|
|
// Set up alert
|
|
if (!global.alert) {
|
|
global.alert = function(text) {
|
|
// Require Alert on demand. Requiring it too early can lead to issues
|
|
// with things like Platform not being fully initialized.
|
|
require('Alert').alert('Alert', '' + text);
|
|
};
|
|
}
|
|
|
|
// Set up Geolocation
|
|
let navigator = global.navigator;
|
|
if (navigator === undefined) {
|
|
global.navigator = navigator = {};
|
|
}
|
|
|
|
// see https://github.com/facebook/react-native/issues/10881
|
|
defineLazyProperty(navigator, 'product', () => 'ReactNative');
|
|
defineLazyProperty(navigator, 'geolocation', () => require('Geolocation'));
|
|
|
|
// Just to make sure the JS gets packaged up. Wait until the JS environment has
|
|
// been initialized before requiring them.
|
|
const BatchedBridge = require('BatchedBridge');
|
|
BatchedBridge.registerLazyCallableModule('Systrace', () => require('Systrace'));
|
|
BatchedBridge.registerLazyCallableModule('JSTimers', () => require('JSTimers'));
|
|
BatchedBridge.registerLazyCallableModule('HeapCapture', () => require('HeapCapture'));
|
|
BatchedBridge.registerLazyCallableModule('SamplingProfiler', () => require('SamplingProfiler'));
|
|
BatchedBridge.registerLazyCallableModule('RCTLog', () => require('RCTLog'));
|
|
BatchedBridge.registerLazyCallableModule('RCTDeviceEventEmitter', () => require('RCTDeviceEventEmitter'));
|
|
BatchedBridge.registerLazyCallableModule('RCTNativeAppEventEmitter', () => require('RCTNativeAppEventEmitter'));
|
|
BatchedBridge.registerLazyCallableModule('PerformanceLogger', () => require('PerformanceLogger'));
|
|
|
|
// Set up devtools
|
|
if (__DEV__) {
|
|
if (!global.__RCTProfileIsProfiling) {
|
|
BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
|
|
|
|
// not when debugging in chrome
|
|
// TODO(t12832058) This check is broken
|
|
if (!window.document) {
|
|
require('setupDevtools');
|
|
}
|
|
|
|
// Set up inspector
|
|
const JSInspector = require('JSInspector');
|
|
JSInspector.registerAgent(require('NetworkAgent'));
|
|
}
|
|
}
|