diff --git a/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js index e8598a6..62bcf45 100644 --- a/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js +++ b/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js @@ -560,7 +560,7 @@ if (global.RN$Bridgeless) { AndroidTextInputNativeComponent = 'AndroidTextInput'; } else { AndroidTextInputNativeComponent = requireNativeComponent( - 'AndroidTextInput', + 'PasteableTextInputAndroid', ); } diff --git a/node_modules/react-native/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h b/node_modules/react-native/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h index 2da6323..1f64ffe 100644 --- a/node_modules/react-native/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +++ b/node_modules/react-native/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h @@ -2796,6 +2796,7 @@ namespace JS { namespace NativeWebSocketModule { struct SpecConnectOptions { id _Nullable headers() const; + NSString *certificate() const; SpecConnectOptions(NSDictionary *const v) : _v(v) {} private: @@ -3862,3 +3863,9 @@ inline id _Nullable JS::NativeWebSocketModule::SpecConnectOptions::hea id const p = _v[@"headers"]; return p; } + +inline NSString *JS::NativeWebSocketModule::SpecConnectOptions::certificate() const +{ + id const p = _v[@"certificate"]; + return p; +} diff --git a/node_modules/react-native/Libraries/Lists/VirtualizedList.js b/node_modules/react-native/Libraries/Lists/VirtualizedList.js index 9ec105f..18f7bf6 100644 --- a/node_modules/react-native/Libraries/Lists/VirtualizedList.js +++ b/node_modules/react-native/Libraries/Lists/VirtualizedList.js @@ -20,6 +20,7 @@ const ScrollView = require('../Components/ScrollView/ScrollView'); const StyleSheet = require('../StyleSheet/StyleSheet'); const View = require('../Components/View/View'); const ViewabilityHelper = require('./ViewabilityHelper'); +const Platform = require('../Utilities/Platform'); const flattenStyle = require('../StyleSheet/flattenStyle'); const infoLog = require('../Utilities/infoLog'); @@ -2186,7 +2187,7 @@ function describeNestedLists(childList: { const styles = StyleSheet.create({ verticallyInverted: { - transform: [{scaleY: -1}], + ...Platform.select({android: {transform: [{perspective: 1}, {scaleY: -1}]}, ios: {transform: [{scaleY: -1}]}}), }, horizontallyInverted: { transform: [{scaleX: -1}], diff --git a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.h b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.h index 1b17cff..70a59b6 100644 --- a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.h +++ b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.h @@ -54,13 +54,14 @@ extern NSString *const RCTSRHTTPResponseErrorKey; @property (nonatomic, readonly) RCTSRReadyState readyState; @property (nonatomic, readonly, strong) NSURL *url; +@property (nonatomic, readonly, strong) NSString *certificate; // This returns the negotiated protocol. // It will be nil until after the handshake completes. @property (nonatomic, readonly, copy) NSString *protocol; // Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. -- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols certificate:(NSString *)certificate NS_DESIGNATED_INITIALIZER; - (instancetype)initWithURLRequest:(NSURLRequest *)request; // Some helper constructors. @@ -88,6 +89,9 @@ extern NSString *const RCTSRHTTPResponseErrorKey; // Send Data (can be nil) in a ping message. - (void)sendPing:(NSData *)data; +// Get the certificate identity from the keyChain by name +- (SecIdentityRef) GetIdentityByName:(NSString *)name; + @end #pragma mark - RCTSRWebSocketDelegate diff --git a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m index b967c14..1a38e00 100644 --- a/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m +++ b/node_modules/react-native/Libraries/WebSocket/RCTSRWebSocket.m @@ -228,7 +228,7 @@ @implementation RCTSRWebSocket RCTSRIOConsumerPool *_consumerPool; } -- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols certificate:(NSString *)certificate { RCTAssertParam(request); @@ -238,6 +238,10 @@ - (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protoc NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:components.URL]; [request setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:cookies]]; } - return [self initWithURLRequest:request protocols:protocols]; + return [self initWithURLRequest:request protocols:protocols certificate:self.certificate]; } - (void)_RCTSR_commonInit @@ -344,6 +348,36 @@ - (void)setReadyState:(RCTSRReadyState)aReadyState #endif +- (SecIdentityRef) GetIdentityByName:(NSString *)name +{ + NSMutableDictionary * query = [[NSMutableDictionary alloc] init]; + + //Set up the invariant pieces of the query + [query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit]; + [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnRef]; + [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; + [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes]; + [query setObject:(id)kSecClassIdentity forKey:(id)kSecClass]; + [query setObject:name forKey:(id)kSecAttrLabel]; + + OSStatus resultCode = noErr; + CFTypeRef result = nil; + //Execute the query saving the results in items. + resultCode = SecItemCopyMatching((CFDictionaryRef)query, &result); + CFDictionaryRef item = (CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)result, 0); + if (item != nil) { + SecIdentityRef identity = nil; + CFTypeRef value; + if(CFDictionaryGetValueIfPresent(item, kSecValueRef, &value)) + { + identity = (SecIdentityRef)value; + } + + return identity; + } + return nil; +} + - (void)open { assert(_url); @@ -515,6 +549,24 @@ - (void)_initializeStreams RCTLogInfo(@"SocketRocket: In debug mode. Allowing connection to any root cert"); #endif + if (_certificate) { + SecIdentityRef identity = [self GetIdentityByName:_certificate]; + if (identity != nil) { + SecCertificateRef certificate = NULL; + OSStatus status = SecIdentityCopyCertificate(identity, &certificate); + if (!status) { + NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identity, (__bridge id)certificate, nil]; + + [SSLOptions setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]; + [SSLOptions setObject:[NSString stringWithFormat:@"%@:%d", host, port] forKey:(NSString *)kCFStreamSSLPeerName]; + [SSLOptions setObject:(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString*)kCFStreamSSLLevel]; + [SSLOptions setObject:(NSString *)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(NSString*)kCFStreamPropertySocketSecurityLevel]; + [SSLOptions setObject:myCerts forKey:(NSString *)kCFStreamSSLCertificates]; + [SSLOptions setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCFStreamSSLIsServer]; + } + } + } + [_outputStream setProperty:SSLOptions forKey:(__bridge id)kCFStreamPropertySSLSettings]; } diff --git a/node_modules/react-native/Libraries/WebSocket/WebSocket.js b/node_modules/react-native/Libraries/WebSocket/WebSocket.js index 0b051c4..d5bbf3f 100644 --- a/node_modules/react-native/Libraries/WebSocket/WebSocket.js +++ b/node_modules/react-native/Libraries/WebSocket/WebSocket.js @@ -91,7 +91,7 @@ class WebSocket extends (EventTarget(...WEBSOCKET_EVENTS): any) { protocols = [protocols]; } - const {headers = {}, ...unrecognized} = options || {}; + const {headers = {}, certificate = {}, ...unrecognized} = options || {}; // Preserve deprecated backwards compatibility for the 'origin' option /* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an @@ -128,7 +128,7 @@ class WebSocket extends (EventTarget(...WEBSOCKET_EVENTS): any) { this._eventEmitter = new NativeEventEmitter(NativeWebSocketModule); this._socketId = nextWebSocketId++; this._registerEvents(); - NativeWebSocketModule.connect(url, protocols, {headers}, this._socketId); + NativeWebSocketModule.connect(url, protocols, {headers, certificate}, this._socketId); } get binaryType(): ?BinaryType { diff --git a/node_modules/react-native/React/CoreModules/RCTWebSocketModule.mm b/node_modules/react-native/React/CoreModules/RCTWebSocketModule.mm index 9648b18..3594b43 100644 --- a/node_modules/react-native/React/CoreModules/RCTWebSocketModule.mm +++ b/node_modules/react-native/React/CoreModules/RCTWebSocketModule.mm @@ -88,7 +88,7 @@ - (void)invalidate }]; } - RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURLRequest:request protocols:protocols]; + RCTSRWebSocket *webSocket = [[RCTSRWebSocket alloc] initWithURLRequest:request protocols:protocols certificate:options.certificate()]; [webSocket setDelegateDispatchQueue:[self methodQueue]]; webSocket.delegate = self; webSocket.reactTag = @(socketID); diff --git a/node_modules/react-native/react.gradle b/node_modules/react-native/react.gradle index 6441d93..4a0cd01 100644 --- a/node_modules/react-native/react.gradle +++ b/node_modules/react-native/react.gradle @@ -157,7 +157,7 @@ afterEvaluate { // Set up dev mode def devEnabled = !(config."devDisabledIn${targetName}" - || targetName.toLowerCase().contains("release")) + || targetName.toLowerCase().contains("release") || targetName.toLowerCase().contains("unsigned")) def extraArgs = config.extraPackagerArgs ?: []; @@ -177,7 +177,7 @@ afterEvaluate { def hermesFlags; def hbcTempFile = file("${jsBundleFile}.hbc") exec { - if (targetName.toLowerCase().contains("release")) { + if (targetName.toLowerCase().contains("release") || targetName.toLowerCase().contains("unsigned")) { // Can't use ?: since that will also substitute valid empty lists hermesFlags = config.hermesFlagsRelease if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"] @@ -221,7 +221,7 @@ afterEvaluate { ? config."bundleIn${targetName}" : config."bundleIn${variant.buildType.name.capitalize()}" != null ? config."bundleIn${variant.buildType.name.capitalize()}" - : targetName.toLowerCase().contains("release") + : (targetName.toLowerCase().contains("release") || targetName.toLowerCase().contains("unsigned")) } // Expose a minimal interface on the application variant and the task itself: @@ -313,7 +313,7 @@ afterEvaluate { // This should really be done by packaging all Hermes releated libs into // two separate HermesDebug and HermesRelease AARs, but until then we'll // kludge it by deleting the .so files out of the /transforms/ directory. - def isRelease = targetName.toLowerCase().contains("release") + def isRelease = targetName.toLowerCase().contains("release") || targetName.toLowerCase().contains("unsigned") def libDir = "$buildDir/intermediates/transforms/" def vmSelectionAction = { fileTree(libDir).matching {