react-native/Libraries/CameraRoll/RCTImagePickerManager.m
Abhinandan 82af7c989b Fix for crash when height or width is nil (#20454)
Summary:
This PR fixes the issue of height/width being nil in line no 128 of RCTImagePickerManager.m .
`    [self _dismissPicker:picker args:tempImageTag ? @[tempImageTag, height, width] : nil];
`
Fixes#20411

Test Plan
----------
To verify the fix , please make the changes to make either height, width or both `nil `
 in `- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info` function of RCTImagePickerManager.m , run the code , you will see the error saying , one of the argument is nil from the array .

![crashscenario](https://user-images.githubusercontent.com/763696/43397014-133ae8fc-9421-11e8-9730-c5906cb8dbea.png)

![crashhandledscenario](https://user-images.githubusercontent.com/763696/43397012-130e42f2-9421-11e8-80fc-cb1abaf8197c.png)

Now run the code with the fix , it will not crash .

Release Notes:
--------------
[IOS][BUGFIX][RCTImagePickerManager] - Change in RCTImagePickerManager to handle crashes if height/width is nil .
Pull Request resolved: https://github.com/facebook/react-native/pull/20454

Differential Revision: D9061059

Pulled By: hramos

fbshipit-source-id: b17f58e411f97f9b904cca0de6c151312c732972
2018-07-30 11:17:08 -07:00

179 lines
5.7 KiB
Objective-C

/*
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#import "RCTImagePickerManager.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTImageStoreManager.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
@interface RCTImagePickerManager () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@end
@implementation RCTImagePickerManager
{
NSMutableArray<UIImagePickerController *> *_pickers;
NSMutableArray<RCTResponseSenderBlock> *_pickerCallbacks;
NSMutableArray<RCTResponseSenderBlock> *_pickerCancelCallbacks;
}
RCT_EXPORT_MODULE(ImagePickerIOS);
@synthesize bridge = _bridge;
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_METHOD(canRecordVideos:(RCTResponseSenderBlock)callback)
{
NSArray<NSString *> *availableMediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
callback(@[@([availableMediaTypes containsObject:(NSString *)kUTTypeMovie])]);
}
RCT_EXPORT_METHOD(canUseCamera:(RCTResponseSenderBlock)callback)
{
callback(@[@([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])]);
}
RCT_EXPORT_METHOD(openCameraDialog:(NSDictionary *)config
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
{
if (RCTRunningInAppExtension()) {
cancelCallback(@[@"Camera access is unavailable in an app extension"]);
return;
}
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
if ([RCTConvert BOOL:config[@"videoMode"]]) {
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
}
[self _presentPicker:imagePicker
successCallback:callback
cancelCallback:cancelCallback];
}
RCT_EXPORT_METHOD(openSelectDialog:(NSDictionary *)config
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
{
if (RCTRunningInAppExtension()) {
cancelCallback(@[@"Image picker is currently unavailable in an app extension"]);
return;
}
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
NSMutableArray<NSString *> *allowedTypes = [NSMutableArray new];
if ([RCTConvert BOOL:config[@"showImages"]]) {
[allowedTypes addObject:(NSString *)kUTTypeImage];
}
if ([RCTConvert BOOL:config[@"showVideos"]]) {
[allowedTypes addObject:(NSString *)kUTTypeMovie];
}
imagePicker.mediaTypes = allowedTypes;
[self _presentPicker:imagePicker
successCallback:callback
cancelCallback:cancelCallback];
}
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
BOOL isMovie = [mediaType isEqualToString:(NSString *)kUTTypeMovie];
NSString *key = isMovie ? UIImagePickerControllerMediaURL : UIImagePickerControllerReferenceURL;
NSURL *imageURL = info[key];
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSNumber *width = 0;
NSNumber *height = 0;
if (image) {
height = @(image.size.height);
width = @(image.size.width);
}
if (imageURL) {
[self _dismissPicker:picker args:@[imageURL.absoluteString, RCTNullIfNil(height), RCTNullIfNil(width)]];
return;
}
// This is a newly taken image, and doesn't have a URL yet.
// We need to save it to the image store first.
UIImage *originalImage = info[UIImagePickerControllerOriginalImage];
// WARNING: Using ImageStoreManager may cause a memory leak because the
// image isn't automatically removed from store once we're done using it.
[_bridge.imageStoreManager storeImage:originalImage withBlock:^(NSString *tempImageTag) {
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag, RCTNullIfNil(height), RCTNullIfNil(width)] : nil];
}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self _dismissPicker:picker args:nil];
}
- (void)_presentPicker:(UIImagePickerController *)imagePicker
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback
{
if (!_pickers) {
_pickers = [NSMutableArray new];
_pickerCallbacks = [NSMutableArray new];
_pickerCancelCallbacks = [NSMutableArray new];
}
[_pickers addObject:imagePicker];
[_pickerCallbacks addObject:callback];
[_pickerCancelCallbacks addObject:cancelCallback];
UIViewController *rootViewController = RCTPresentedViewController();
[rootViewController presentViewController:imagePicker animated:YES completion:nil];
}
- (void)_dismissPicker:(UIImagePickerController *)picker args:(NSArray *)args
{
NSUInteger index = [_pickers indexOfObject:picker];
if (index == NSNotFound) {
// This happens if the user selects multiple items in succession.
return;
}
RCTResponseSenderBlock successCallback = _pickerCallbacks[index];
RCTResponseSenderBlock cancelCallback = _pickerCancelCallbacks[index];
[_pickers removeObjectAtIndex:index];
[_pickerCallbacks removeObjectAtIndex:index];
[_pickerCancelCallbacks removeObjectAtIndex:index];
UIViewController *rootViewController = RCTPresentedViewController();
[rootViewController dismissViewControllerAnimated:YES completion:nil];
if (args) {
successCallback(args);
} else {
cancelCallback(@[]);
}
}
@end