Title: [185241] trunk/Source/WebKit2
Revision
185241
Author
jhoneyc...@apple.com
Date
2015-06-04 23:44:55 -0700 (Thu, 04 Jun 2015)

Log Message

[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.

Modified Paths

Diff

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
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to