⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 242369 in webkit


Ignore:
Timestamp:
Mar 4, 2019, 12:20:59 PM (7 years ago)
Author:
Chris Dumez
Message:

[iOS] Improve our file picker
https://bugs.webkit.org/show_bug.cgi?id=195284
<rdar://problem/45655856>

Reviewed by Tim Horton and Wenson Hsieh.

Source/WebCore:

Export UTIUtilities.h so that it can be used from WebKit2.

  • WebCore.xcodeproj/project.pbxproj:

Source/WebKit:

Improve our file picker on iOS so that:

  • Accepted file extensions specified in the HTML (e.g. <input type="file"> accept=".pdf">) are now properly reflected in the file picker. Previously, we only we only supported MIME types in the accept attribute that only Image / Video ones.
  • If accepted types are specified in the HTML and not of them are Video or Image types, then bypass the UIDocumentMenuViewController and show the file picker directly (as if the user had tapped on "Browse..." on that menu). Other menu items such as "Take Photo or Video" and "Photo Library" do not make sense if the page only accepts PDF files for example.

Things that we should do but are not fixed in this patch:

  • Stop using UIDocumentMenuViewController entirely since it was deprecated in favor of using UIDocumentPickerViewController directly.
  • Add multiple selection support, which is supported both on the HTML side and in the UIDocumentPickerViewController API.
  • UIProcess/ios/forms/WKFileUploadPanel.mm:

(arrayContainsUTIThatConformsTo):
(-[WKFileUploadPanel dealloc]):
(-[WKFileUploadPanel presentWithParameters:resultListener:]):
(UTIsForMIMETypes):
(-[WKFileUploadPanel _mediaTypesForPickerSourceType:]):
(-[WKFileUploadPanel _cameraButtonLabelAllowingPhoto:allowingVideo:]):
(-[WKFileUploadPanel _showDocumentPickerMenu]):

Location:
trunk/Source
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r242366 r242369  
     12019-03-04  Chris Dumez  <cdumez@apple.com>
     2
     3        [iOS] Improve our file picker
     4        https://bugs.webkit.org/show_bug.cgi?id=195284
     5        <rdar://problem/45655856>
     6
     7        Reviewed by Tim Horton and Wenson Hsieh.
     8
     9        Export UTIUtilities.h so that it can be used from WebKit2.
     10
     11        * WebCore.xcodeproj/project.pbxproj:
     12
    1132019-03-04  Zalan Bujtas  <zalan@apple.com>
    214
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r242359 r242369  
    661661                1F72BF0B187FD45C0009BCB3 /* TileControllerMemoryHandlerIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F72BF09187FD4270009BCB3 /* TileControllerMemoryHandlerIOS.h */; settings = {ATTRIBUTES = (Private, ); }; };
    662662                1F8756B21E22C3350042C40D /* WebSQLiteDatabaseTrackerClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F8756B11E22BEEF0042C40D /* WebSQLiteDatabaseTrackerClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
    663                 1FAFBF1915A5FA7400083A20 /* UTIUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FAFBF1615A5FA5200083A20 /* UTIUtilities.h */; };
     663                1FAFBF1915A5FA7400083A20 /* UTIUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FAFBF1615A5FA5200083A20 /* UTIUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
    664664                1FC40FBA1655CCB90040F29E /* SubimageCacheWithTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC40FB71655C5910040F29E /* SubimageCacheWithTimer.h */; };
    665665                20D629271253690B00081543 /* InspectorInstrumentation.h in Headers */ = {isa = PBXBuildFile; fileRef = 20D629251253690B00081543 /* InspectorInstrumentation.h */; };
  • trunk/Source/WebCore/platform/network/mac/UTIUtilities.h

    r220808 r242369  
    3333String MIMETypeFromUTI(const String&);
    3434String MIMETypeFromUTITree(const String&);
    35 String UTIFromMIMEType(const String&);
     35WEBCORE_EXPORT String UTIFromMIMEType(const String&);
    3636bool isDeclaredUTI(const String&);
    3737}
  • trunk/Source/WebKit/ChangeLog

    r242367 r242369  
     12019-03-04  Chris Dumez  <cdumez@apple.com>
     2
     3        [iOS] Improve our file picker
     4        https://bugs.webkit.org/show_bug.cgi?id=195284
     5        <rdar://problem/45655856>
     6
     7        Reviewed by Tim Horton and Wenson Hsieh.
     8
     9        Improve our file picker on iOS so that:
     10        - Accepted file extensions specified in the HTML (e.g. <input type="file"> accept=".pdf">) are now
     11          properly reflected in the file picker. Previously, we only we only supported MIME types in the
     12          accept attribute that only Image / Video ones.
     13        - If accepted types are specified in the HTML and not of them are Video or Image types, then bypass
     14          the UIDocumentMenuViewController and show the file picker directly (as if the user had tapped on
     15          "Browse..." on that menu). Other menu items such as "Take Photo or Video" and "Photo Library" do
     16          not make sense if the page only accepts PDF files for example.
     17
     18        Things that we should do but are not fixed in this patch:
     19        - Stop using UIDocumentMenuViewController entirely since it was deprecated in favor of using
     20          UIDocumentPickerViewController directly.
     21        - Add multiple selection support, which is supported both on the HTML side and in the
     22          UIDocumentPickerViewController API.
     23
     24        * UIProcess/ios/forms/WKFileUploadPanel.mm:
     25        (arrayContainsUTIThatConformsTo):
     26        (-[WKFileUploadPanel dealloc]):
     27        (-[WKFileUploadPanel presentWithParameters:resultListener:]):
     28        (UTIsForMIMETypes):
     29        (-[WKFileUploadPanel _mediaTypesForPickerSourceType:]):
     30        (-[WKFileUploadPanel _cameraButtonLabelAllowingPhoto:allowingVideo:]):
     31        (-[WKFileUploadPanel _showDocumentPickerMenu]):
     32
    1332019-03-04  Alex Christensen  <achristensen@webkit.org>
    234
  • trunk/Source/WebKit/UIProcess/ios/forms/WKFileUploadPanel.mm

    r240729 r242369  
    4242#import "WebPageProxy.h"
    4343#import <MobileCoreServices/MobileCoreServices.h>
     44#import <UIKit/UIDocumentPickerViewController.h>
    4445#import <WebCore/LocalizedStrings.h>
     46#import <WebCore/MIMETypeRegistry.h>
     47#import <WebCore/UTIUtilities.h>
    4548#import <wtf/RetainPtr.h>
     49#import <wtf/text/StringView.h>
    4650
    4751using namespace WebKit;
     
    5256{
    5357    return mediaCaptureType == WebCore::MediaCaptureTypeUser ? UIImagePickerControllerCameraDeviceFront : UIImagePickerControllerCameraDeviceRear;
     58}
     59
     60static bool arrayContainsUTIThatConformsTo(NSArray<NSString *> *typeIdentifiers, CFStringRef conformToUTI)
     61{
     62    for (NSString *uti in typeIdentifiers) {
     63        if (UTTypeConformsTo((__bridge CFStringRef)uti, conformToUTI))
     64            return true;
     65    }
     66    return false;
    5467}
    5568
     
    164177    ALLOW_DEPRECATED_DECLARATIONS_END
    165178    RetainPtr<UIDocumentMenuViewController> _documentMenuController;
     179    RetainPtr<UIDocumentPickerViewController> _documentPickerController;
    166180    WebCore::MediaCaptureType _mediaCaptureType;
    167181}
     
    180194    [_presentationPopover setDelegate:nil];
    181195    [_documentMenuController setDelegate:nil];
     196    [_documentPickerController setDelegate:nil];
    182197
    183198    [super dealloc];
     
    234249    for (auto mimeType : acceptMimeTypes->elementsOfType<API::String>())
    235250        [mimeTypes addObject:mimeType->string()];
     251
     252    Ref<API::Array> acceptFileExtensions = parameters->acceptFileExtensions();
     253    for (auto extension : acceptFileExtensions->elementsOfType<API::String>()) {
     254        String mimeType = WebCore::MIMETypeRegistry::getMIMETypeForExtension(extension->stringView().substring(1).toString());
     255        if (!mimeType.isEmpty())
     256            [mimeTypes addObject:mimeType];
     257    }
     258
    236259    _mimeTypes = adoptNS([mimeTypes copy]);
    237260
     
    292315#pragma mark - Media Types
    293316
    294 static bool stringHasPrefixCaseInsensitive(NSString *str, NSString *prefix)
    295 {
    296     NSRange range = [str rangeOfString:prefix options:(NSCaseInsensitiveSearch | NSAnchoredSearch)];
    297     return range.location != NSNotFound;
    298 }
    299 
    300317static NSArray *UTIsForMIMETypes(NSArray *mimeTypes)
    301318{
    302     // The HTML5 spec mentions the literal "image/*" and "video/*" strings.
    303     // We support these and go a step further, if the MIME type starts with
    304     // "image/" or "video/" we adjust the picker's image or video filters.
    305     // So, "image/jpeg" would make the picker display all images types.
    306319    NSMutableSet *mediaTypes = [NSMutableSet set];
    307320    for (NSString *mimeType in mimeTypes) {
    308         // FIXME: We should support more MIME type -> UTI mappings. <http://webkit.org/b/142614>
    309         if (stringHasPrefixCaseInsensitive(mimeType, @"image/"))
    310             [mediaTypes addObject:(NSString *)kUTTypeImage];
    311         else if (stringHasPrefixCaseInsensitive(mimeType, @"video/"))
    312             [mediaTypes addObject:(NSString *)kUTTypeMovie];
    313     }
    314 
     321        auto uti = WebCore::UTIFromMIMEType(mimeType);
     322        if (!uti.isEmpty())
     323            [mediaTypes addObject:(__bridge NSString *)uti];
     324    }
    315325    return mediaTypes.allObjects;
    316326}
     
    326336}
    327337
    328 - (NSArray *)_documentPickerMenuMediaTypes
     338#pragma mark - Source selection menu
     339
     340- (NSString *)_photoLibraryButtonLabel
     341{
     342    return WEB_UI_STRING_KEY("Photo Library", "Photo Library (file upload action sheet)", "File Upload alert sheet button string for choosing an existing media item from the Photo Library");
     343}
     344
     345- (NSString *)_cameraButtonLabelAllowingPhoto:(BOOL)allowPhoto allowingVideo:(BOOL)allowVideo
     346{
     347    ASSERT(allowPhoto || allowVideo);
     348    if (allowPhoto && allowVideo)
     349        return WEB_UI_STRING_KEY("Take Photo or Video", "Take Photo or Video (file upload action sheet)", "File Upload alert sheet camera button string for taking photos or videos");
     350
     351    if (allowVideo)
     352        return WEB_UI_STRING_KEY("Take Video", "Take Video (file upload action sheet)", "File Upload alert sheet camera button string for taking only videos");
     353
     354    return WEB_UI_STRING_KEY("Take Photo", "Take Photo (file upload action sheet)", "File Upload alert sheet camera button string for taking only photos");
     355}
     356
     357- (void)_showDocumentPickerMenu
    329358{
    330359    NSArray *mediaTypes = UTIsForMIMETypes(_mimeTypes.get());
    331     if (mediaTypes.count)
    332         return mediaTypes;
    333 
    334     // Fallback to every supported media type if there is no filter.
    335     return @[@"public.item"];
    336 }
    337 
    338 #pragma mark - Source selection menu
    339 
    340 - (NSString *)_photoLibraryButtonLabel
    341 {
    342     return WEB_UI_STRING_KEY("Photo Library", "Photo Library (file upload action sheet)", "File Upload alert sheet button string for choosing an existing media item from the Photo Library");
    343 }
    344 
    345 - (NSString *)_cameraButtonLabel
    346 {
    347     if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    348         return nil;
    349 
    350     // Choose the appropriate string for the camera button.
    351     NSArray *filteredMediaTypes = [self _mediaTypesForPickerSourceType:UIImagePickerControllerSourceTypeCamera];
    352     BOOL containsImageMediaType = [filteredMediaTypes containsObject:(NSString *)kUTTypeImage];
    353     BOOL containsVideoMediaType = [filteredMediaTypes containsObject:(NSString *)kUTTypeMovie];
    354     ASSERT(containsImageMediaType || containsVideoMediaType);
    355     if (containsImageMediaType && containsVideoMediaType)
    356         return WEB_UI_STRING_KEY("Take Photo or Video", "Take Photo or Video (file upload action sheet)", "File Upload alert sheet camera button string for taking photos or videos");
    357 
    358     if (containsVideoMediaType)
    359         return WEB_UI_STRING_KEY("Take Video", "Take Video (file upload action sheet)", "File Upload alert sheet camera button string for taking only videos");
    360 
    361     return WEB_UI_STRING_KEY("Take Photo", "Take Photo (file upload action sheet)", "File Upload alert sheet camera button string for taking only photos");
    362 }
    363 
    364 - (void)_showDocumentPickerMenu
    365 {
    366     // FIXME: Support multiple file selection when implemented. <rdar://17177981>
    367     _documentMenuController = adoptNS([[UIDocumentMenuViewController alloc] _initIgnoringApplicationEntitlementForImportOfTypes:[self _documentPickerMenuMediaTypes]]);
    368     [_documentMenuController setDelegate:self];
    369 
    370     [_documentMenuController addOptionWithTitle:[self _photoLibraryButtonLabel] image:photoLibraryIcon() order:UIDocumentMenuOrderFirst handler:^{
    371         [self _showPhotoPickerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
    372     }];
    373 
    374     if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
    375         if (NSString *cameraString = [self _cameraButtonLabel]) {
     360
     361    BOOL containsImageMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeImage);
     362    BOOL containsVideoMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeMovie);
     363
     364    NSArray *documentTypes = mediaTypes.count ? mediaTypes : @[(__bridge NSString *)kUTTypeItem];
     365    if (containsImageMediaType || containsVideoMediaType) {
     366        // FIXME: UIDocumentMenuViewController is deprecated, we should use UIDocumentPickerViewController instead.
     367        // FIXME: Support multiple file selection when implemented. <rdar://17177981>
     368        _documentMenuController = adoptNS([[UIDocumentMenuViewController alloc] _initIgnoringApplicationEntitlementForImportOfTypes:documentTypes]);
     369        [_documentMenuController setDelegate:self];
     370
     371        [_documentMenuController addOptionWithTitle:[self _photoLibraryButtonLabel] image:photoLibraryIcon() order:UIDocumentMenuOrderFirst handler:^{
     372            [self _showPhotoPickerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
     373        }];
     374
     375        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
     376            NSString *cameraString = [self _cameraButtonLabelAllowingPhoto:containsImageMediaType allowingVideo:containsVideoMediaType];
    376377            [_documentMenuController addOptionWithTitle:cameraString image:cameraIcon() order:UIDocumentMenuOrderFirst handler:^{
    377378                _usingCamera = YES;
     
    379380            }];
    380381        }
    381     }
    382 
    383     [self _presentMenuOptionForCurrentInterfaceIdiom:_documentMenuController.get()];
    384     // Clear out the view controller we just presented. Don't save a reference to the UIDocumentMenuViewController as it is self dismissing.
     382
     383        [self _presentMenuOptionForCurrentInterfaceIdiom:_documentMenuController.get()];
     384    } else {
     385        // Image and Video types are not accepted so bypass the menu and open the file picker directly.
     386        // FIXME: Support multiple file selection when implemented. <rdar://17177981>
     387        _documentPickerController = adoptNS([[UIDocumentPickerViewController alloc] initWithDocumentTypes:documentTypes inMode:UIDocumentPickerModeImport]);
     388        [_documentPickerController setDelegate:self];
     389        [self _presentFullscreenViewController:_documentPickerController.get() animated:YES];
     390    }
     391
     392    // Clear out the view controller we just presented. Don't save a reference to the UIDocumentMenuViewController / UIDocumentPickerViewController as it is self dismissing.
    385393    _presentationViewController = nil;
    386394}
Note: See TracChangeset for help on using the changeset viewer.