Modified: trunk/Source/WebKit2/ChangeLog (185240 => 185241)
--- trunk/Source/WebKit2/ChangeLog 2015-06-05 05:20:45 UTC (rev 185240)
+++ trunk/Source/WebKit2/ChangeLog 2015-06-05 06:44:55 UTC (rev 185241)
@@ -1,3 +1,48 @@
+2015-06-01 Jon Honeycutt <jhoneyc...@apple.com>
+
+ [iOS] Uploading an animated GIF from the photo library uploads a JPEG
+
+ <https://bugs.webkit.org/show_bug.cgi?id=145539>
+ <rdar://problem/19760877
+
+ Selecting a photo from the photo library using UIImagePickerController
+ results in an NSDictionary with image info and a UIImage with the
+ image. It's not possible to get the original image data from the
+ UIImage, only a PNG or JPEG representation, so we were getting a JPEG
+ representation and uploading that to the server. For animated GIFs,
+ this meant a JPEG representation of the first frame.
+
+ To fix this, we'll get the asset URL, if available, and use the Photos
+ API to query for the original image data.
+
+ Reviewed by Andy Estes and Joe Pecoraro.
+
+ * UIProcess/ios/forms/WKFileUploadPanel.mm:
+ Soft link the Photos framework.
+
+ (-[WKFileUploadPanel _uploadItemForImageData:originalImage:imageName:successBlock:failureBlock:]):
+ Save the image's data to a temporary location, and create a
+ _WKImageFileUploadItem. Code moved from
+ -_uploadItemFromMediaInfo:successBlock:failureBlock:.
+
+ (-[WKFileUploadPanel _uploadItemForJPEGRepresentationOfImage:successBlock:failureBlock:]):
+ Get a JPEG representation of the UIImage, and create an upload item
+ from that data.
+
+ (uploadImageNameFromUTI):
+ Return a filename for the uploaded image based on the UTI.
+
+ (-[WKFileUploadPanel _uploadItemForImage:withAssetURL:successBlock:failureBlock:]):
+ Fetch the photo asset with the given asset URL. Create an image manager
+ object, and request the image data for the photo asset. Create an
+ upload item from that data. If we fail, fall back to a JPEG
+ representation.
+
+ (-[WKFileUploadPanel _uploadItemFromMediaInfo:successBlock:failureBlock:]):
+ Return early if the media item is not an image type. If it is, and we
+ have an asset URL, try to create an upload item for the native image.
+ If we don't have an asset URL, use a JPEG representation of the image.
+
2015-06-04 Gyuyoung Kim <gyuyoung....@webkit.org>
REGRESSION(r185091): Crash happens on indexdb tests
Modified: trunk/Source/WebKit2/UIProcess/ios/forms/WKFileUploadPanel.mm (185240 => 185241)
--- trunk/Source/WebKit2/UIProcess/ios/forms/WKFileUploadPanel.mm 2015-06-05 05:20:45 UTC (rev 185240)
+++ trunk/Source/WebKit2/UIProcess/ios/forms/WKFileUploadPanel.mm 2015-06-05 06:44:55 UTC (rev 185241)
@@ -42,6 +42,7 @@
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
#import <MobileCoreServices/MobileCoreServices.h>
+#import <Photos/Photos.h>
#import <WebCore/LocalizedStrings.h>
#import <WebCore/SoftLinking.h>
#import <WebKit/WebNSFileManagerExtras.h>
@@ -55,6 +56,14 @@
SOFT_LINK_FRAMEWORK(CoreMedia);
SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime);
+
+SOFT_LINK_FRAMEWORK(Photos);
+SOFT_LINK_CLASS(Photos, PHAsset);
+SOFT_LINK_CLASS(Photos, PHImageManager);
+SOFT_LINK_CLASS(Photos, PHImageRequestOptions);
+SOFT_LINK_CONSTANT(Photos, PHImageRequestOptionsResizeModeNone, NSString *);
+SOFT_LINK_CONSTANT(Photos, PHImageRequestOptionsVersionCurrent, NSString *);
+
#define kCMTimeZero getkCMTimeZero()
#pragma mark - Document picker icons
@@ -726,6 +735,93 @@
[self _uploadItemFromMediaInfo:info successBlock:uploadItemSuccessBlock failureBlock:failureBlock];
}
+- (void)_uploadItemForImageData:(NSData *)imageData originalImage:(UIImage *)originalImage imageName:(NSString *)imageName successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
+{
+ ASSERT_ARG(imageData, imageData);
+ ASSERT_ARG(originalImage, originalImage);
+ ASSERT(!isMainThread());
+
+ NSString * const kTemporaryDirectoryName = @"WKWebFileUpload";
+
+ // Build temporary file path.
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSString *temporaryDirectory = [fileManager _webkit_createTemporaryDirectoryWithTemplatePrefix:kTemporaryDirectoryName];
+ NSString *filePath = [temporaryDirectory stringByAppendingPathComponent:imageName];
+ if (!filePath) {
+ LOG_ERROR("WKFileUploadPanel: Failed to create temporary directory to save image");
+ failureBlock();
+ return;
+ }
+
+ // Save the image to the temporary file.
+ NSError *error = nil;
+ [imageData writeToFile:filePath options:NSDataWritingAtomic error:&error];
+ if (error) {
+ LOG_ERROR("WKFileUploadPanel: Error writing image data to temporary file: %@", error);
+ failureBlock();
+ return;
+ }
+
+ successBlock(adoptNS([[_WKImageFileUploadItem alloc] initWithFileURL:[NSURL fileURLWithPath:filePath] originalImage:originalImage]).get());
+}
+
+- (void)_uploadItemForJPEGRepresentationOfImage:(UIImage *)image successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
+{
+ ASSERT_ARG(image, image);
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ // FIXME: Different compression for different devices?
+ // FIXME: Different compression for different UIImage sizes?
+ // FIXME: Should EXIF data be maintained?
+ const CGFloat compression = 0.8;
+ NSData *jpeg = UIImageJPEGRepresentation(image, compression);
+ if (!jpeg) {
+ LOG_ERROR("WKFileUploadPanel: Failed to create JPEG representation for image");
+ failureBlock();
+ return;
+ }
+
+ // FIXME: Should we get the photo asset and get the actual filename for the photo instead of
+ // naming each of the individual uploads image.jpg? This won't work for photos taken with
+ // the camera, but would work for photos picked from the library.
+ NSString * const kUploadImageName = @"image.jpg";
+ [self _uploadItemForImageData:jpeg originalImage:image imageName:kUploadImageName successBlock:successBlock failureBlock:failureBlock];
+ });
+}
+
+- (void)_uploadItemForImage:(UIImage *)image withAssetURL:(NSURL *)assetURL successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
+{
+ ASSERT_ARG(image, image);
+ ASSERT_ARG(assetURL, assetURL);
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ PHFetchResult *result = [getPHAssetClass() fetchAssetsWithALAssetURLs:@[assetURL] options:nil];
+ if (!result.count) {
+ LOG_ERROR("WKFileUploadPanel: Failed to fetch asset with URL %@", assetURL);
+ [self _uploadItemForJPEGRepresentationOfImage:image successBlock:successBlock failureBlock:failureBlock];
+ return;
+ }
+
+ RetainPtr<PHImageRequestOptions> options = adoptNS([allocPHImageRequestOptionsInstance() init]);
+ [options setVersion:PHImageRequestOptionsVersionCurrent];
+ [options setSynchronous:YES];
+ [options setResizeMode:PHImageRequestOptionsResizeModeNone];
+
+ PHImageManager *manager = (PHImageManager *)[getPHImageManagerClass() defaultManager];
+ [manager requestImageDataForAsset:result[0] options:options.get() resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation, NSDictionary *info) {
+ if (!imageData) {
+ LOG_ERROR("WKFileUploadPanel: Failed to request image data for asset with URL %@", assetURL);
+ [self _uploadItemForJPEGRepresentationOfImage:image successBlock:successBlock failureBlock:failureBlock];
+ return;
+ }
+
+ RetainPtr<CFStringRef> extension = adoptCF(UTTypeCopyPreferredTagWithClass((CFStringRef)dataUTI, kUTTagClassFilenameExtension));
+ NSString *imageName = [@"image." stringByAppendingString:(extension ? (id)extension.get() : @"jpg")];
+ [self _uploadItemForImageData:imageData originalImage:image imageName:imageName successBlock:successBlock failureBlock:failureBlock];
+ }];
+ });
+}
+
- (void)_uploadItemFromMediaInfo:(NSDictionary *)info successBlock:(void (^)(_WKFileUploadItem *))successBlock failureBlock:(void (^)(void))failureBlock
{
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
@@ -744,62 +840,30 @@
return;
}
- // For images, we create a temporary file path and use the original image.
- if (UTTypeConformsTo((CFStringRef)mediaType, kUTTypeImage)) {
- UIImage *originalImage = [info objectForKey:UIImagePickerControllerOriginalImage];
- if (!originalImage) {
- LOG_ERROR("WKFileUploadPanel: Expected image data but there was none");
- ASSERT_NOT_REACHED();
- failureBlock();
- return;
- }
+ if (!UTTypeConformsTo((CFStringRef)mediaType, kUTTypeImage)) {
+ LOG_ERROR("WKFileUploadPanel: Unexpected media type. Expected image or video, got: %@", mediaType);
+ ASSERT_NOT_REACHED();
+ failureBlock();
+ return;
+ }
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSString * const kTemporaryDirectoryName = @"WKWebFileUpload";
- NSString * const kUploadImageName = @"image.jpg";
+ UIImage *originalImage = [info objectForKey:UIImagePickerControllerOriginalImage];
+ if (!originalImage) {
+ LOG_ERROR("WKFileUploadPanel: Expected image data but there was none");
+ ASSERT_NOT_REACHED();
+ failureBlock();
+ return;
+ }
- // Build temporary file path.
- // FIXME: Should we get the ALAsset for the mediaURL and get the actual filename for the photo
- // instead of naming each of the individual uploads image.jpg? This won't work for photos
- // taken with the camera, but would work for photos picked from the library.
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSString *temporaryDirectory = [fileManager _webkit_createTemporaryDirectoryWithTemplatePrefix:kTemporaryDirectoryName];
- NSString *filePath = [temporaryDirectory stringByAppendingPathComponent:kUploadImageName];
- if (!filePath) {
- LOG_ERROR("WKFileUploadPanel: Failed to create temporary directory to save image");
- failureBlock();
- return;
- }
-
- // Compress to JPEG format.
- // FIXME: Different compression for different devices?
- // FIXME: Different compression for different UIImage sizes?
- // FIXME: Should EXIF data be maintained?
- const CGFloat compression = 0.8;
- NSData *jpeg = UIImageJPEGRepresentation(originalImage, compression);
- if (!jpeg) {
- LOG_ERROR("WKFileUploadPanel: Failed to create JPEG representation for image");
- failureBlock();
- return;
- }
-
- // Save the image to the temporary file.
- NSError *error = nil;
- [jpeg writeToFile:filePath options:NSDataWritingAtomic error:&error];
- if (error) {
- LOG_ERROR("WKFileUploadPanel: Error writing image data to temporary file: %@", error);
- failureBlock();
- return;
- }
-
- successBlock(adoptNS([[_WKImageFileUploadItem alloc] initWithFileURL:[NSURL fileURLWithPath:filePath] originalImage:originalImage]).get());
- });
+ // If we have an asset URL, try to upload the native image.
+ NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];
+ if (referenceURL) {
+ [self _uploadItemForImage:originalImage withAssetURL:referenceURL successBlock:successBlock failureBlock:failureBlock];
return;
}
- // Unknown media type.
- LOG_ERROR("WKFileUploadPanel: Unexpected media type. Expected image or video, got: %@", mediaType);
- failureBlock();
+ // Photos taken with the camera will not have an asset URL. Fall back to a JPEG representation.
+ [self _uploadItemForJPEGRepresentationOfImage:originalImage successBlock:successBlock failureBlock:failureBlock];
}
- (NSString *)_displayStringForPhotos:(NSUInteger)imageCount videos:(NSUInteger)videoCount