Changeset 262895 in webkit


Ignore:
Timestamp:
Jun 10, 2020, 11:31:53 PM (4 years ago)
Author:
commit-queue@webkit.org
Message:

[macOS]: The File Picker of the <input> file element should show the selection filter
https://bugs.webkit.org/show_bug.cgi?id=212485

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2020-06-10
Reviewed by Darin Adler.

Source/WebCore/PAL:

Expose an SPI from CoreServcies to get all the UTIs known by the system.

  • pal/spi/cocoa/CoreServicesSPI.h:

Source/WebKit:

Add two private helpers to WKOpenPanelParameters.

  • UIProcess/API/Cocoa/WKOpenPanelParameters.mm:

(-[WKOpenPanelParameters _allowedFileExtensions]):
Returns an array of strings representing the allowed extensions.

(-[WKOpenPanelParameters _allowedFileExtensionsDescription]):
Returns a string representing a description for the allowed extensions.

  • UIProcess/API/Cocoa/WKOpenPanelParametersPrivate.h:

Tools:

Create a view with an NSPopupButton for the file extensions filter. Set
this view as the AccessoryView of the OpenPanel. Create a customized target
for the NSPopupButton to respond to changing its selection.

  • MiniBrowser/mac/WK2BrowserWindowController.m:

(-[FileExtensionsPopupTarget initWithOpenPanel:allowedFileExtensions:]):
(-[FileExtensionsPopupTarget popupAction:]):
(-[FileExtensionsPopupTarget dealloc]):
(-[WK2BrowserWindowController dealloc]):
(-[WK2BrowserWindowController createFilterView:popupTarget:]):
(-[WK2BrowserWindowController webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:]):

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/PAL/ChangeLog

    r262832 r262895  
     12020-06-10  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        [macOS]: The File Picker of the <input> file element should show the selection filter
     4        https://bugs.webkit.org/show_bug.cgi?id=212485
     5
     6        Reviewed by Darin Adler.
     7
     8        Expose an SPI from CoreServcies to get all the UTIs known by the system.
     9
     10        * pal/spi/cocoa/CoreServicesSPI.h:
     11
    1122020-06-09  Tim Horton  <timothy_horton@apple.com>
    213
  • trunk/Source/WebCore/PAL/pal/spi/cocoa/CoreServicesSPI.h

    r259398 r262895  
    2727
    2828extern "C" void _CSCheckFixDisable();
     29extern "C" CFArrayRef _UTCopyDeclaredTypeIdentifiers(void);
     30
  • trunk/Source/WebKit/ChangeLog

    r262894 r262895  
     12020-06-10  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        [macOS]: The File Picker of the <input> file element should show the selection filter
     4        https://bugs.webkit.org/show_bug.cgi?id=212485
     5
     6        Reviewed by Darin Adler.
     7
     8        Add two private helpers to WKOpenPanelParameters.
     9
     10        * UIProcess/API/Cocoa/WKOpenPanelParameters.mm:
     11        (-[WKOpenPanelParameters _allowedFileExtensions]):
     12        Returns an array of strings representing the allowed extensions.
     13
     14        (-[WKOpenPanelParameters _allowedFileExtensionsDescription]):
     15        Returns a string representing a description for the allowed extensions.
     16
     17        * UIProcess/API/Cocoa/WKOpenPanelParametersPrivate.h:
     18
    1192020-06-10  Beth Dakin  <bdakin@apple.com>
    220
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKOpenPanelParameters.mm

    r242339 r262895  
    11/*
    2  * Copyright (C) 2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2016-2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626#import "config.h"
    2727#import "WKOpenPanelParametersInternal.h"
     28#import <pal/spi/cocoa/CoreServicesSPI.h>
    2829
    2930#if PLATFORM(MAC)
    3031
    3132#import "WKNSArray.h"
     33
     34static NSDictionary<NSString *, NSSet<NSString *> *> *extensionsForMIMETypeMap()
     35{
     36    static auto extensionsForMIMETypeMap = makeNeverDestroyed([] {
     37        auto extensionsForMIMETypeMap = adoptNS([[NSMutableDictionary alloc] init]);
     38        auto allUTIs = adoptCF(_UTCopyDeclaredTypeIdentifiers());
     39
     40        auto addExtensionForMIMEType = ^(NSString *mimeType, NSString *extension) {
     41            if (!extensionsForMIMETypeMap.get()[mimeType])
     42                extensionsForMIMETypeMap.get()[mimeType] = [NSMutableSet set];
     43            [extensionsForMIMETypeMap.get()[mimeType] addObject:extension];
     44        };
     45
     46        auto addExtensionsForMIMEType = ^(NSString *mimeType, NSArray<NSString *> *extensions) {
     47            auto wildcardMIMEType = [[mimeType componentsSeparatedByString:@"/"][0] stringByAppendingString:@"/*"];
     48
     49            for (NSString *extension in extensions) {
     50                if (!extension)
     51                    continue;
     52                // Add extension to wildcardMIMEType, for example add "png" to "image/*"
     53                addExtensionForMIMEType(wildcardMIMEType, extension);
     54                // Add extension to itsmimeType, for example add "png" to "image/png"
     55                addExtensionForMIMEType(mimeType, extension);
     56            }
     57        };
     58
     59        for (CFIndex i = 0, count = CFArrayGetCount(allUTIs.get()); i < count; ++i) {
     60            auto uti = static_cast<CFStringRef>(CFArrayGetValueAtIndex(allUTIs.get(), i));
     61            auto mimeType = adoptCF(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
     62            if (!mimeType)
     63                continue;
     64            auto extensions = adoptCF(UTTypeCopyAllTagsWithClass(uti, kUTTagClassFilenameExtension));
     65            addExtensionsForMIMEType((__bridge NSString *)mimeType.get(), (__bridge NSArray<NSString *> *)extensions.get());
     66        }
     67
     68        // Add additional mime types which _UTCopyDeclaredTypeIdentifiers() may not return.
     69        addExtensionForMIMEType(@"image/webp", @"webp");
     70
     71        return extensionsForMIMETypeMap;
     72    }());
     73
     74    return extensionsForMIMETypeMap.get().get();
     75}
     76
     77static NSSet<NSString *> *extensionsForMIMEType(NSString *mimetype)
     78{
     79    return [extensionsForMIMETypeMap() objectForKey:mimetype];
     80}
    3281
    3382@implementation WKOpenPanelParameters
     
    64113}
    65114
     115- (NSArray<NSString *> *)_allowedFileExtensions
     116{
     117    // Aggregate extensions based on specified MIME types.
     118    auto acceptedMIMETypes = [self _acceptedMIMETypes];
     119    auto acceptedFileExtensions = [self _acceptedFileExtensions];
     120
     121    auto allowedFileExtensions = adoptNS([[NSMutableSet alloc] init]);
     122
     123    [acceptedMIMETypes enumerateObjectsUsingBlock:^(NSString *mimeType, NSUInteger index, BOOL* stop) {
     124        ASSERT([mimeType containsString:@"/"]);
     125        [allowedFileExtensions unionSet:extensionsForMIMEType(mimeType)];
     126    }];
     127
     128    auto additionalAllowedFileExtensions = adoptNS([[NSMutableArray alloc] init]);
     129
     130    [acceptedFileExtensions enumerateObjectsUsingBlock:^(NSString *extension, NSUInteger index, BOOL *stop) {
     131        ASSERT([extension characterAtIndex:0] == '.');
     132        [additionalAllowedFileExtensions addObject:[extension substringFromIndex:1]];
     133    }];
     134   
     135    [allowedFileExtensions addObjectsFromArray:additionalAllowedFileExtensions.get()];
     136    return [allowedFileExtensions allObjects];
     137}
     138
     139- (NSArray<NSString *> *)_allowedFileExtensionsTitles
     140{
     141    auto acceptedMIMETypes = [self _acceptedMIMETypes];
     142    auto acceptedFileExtensions = [self _acceptedFileExtensions];
     143
     144    constexpr auto AllFilesTitle = @"All Files";
     145    constexpr auto CustomFilesTitle = @"Custom Files";
     146   
     147    if (![acceptedMIMETypes count] && ![acceptedFileExtensions count])
     148        return @[AllFilesTitle];
     149
     150    if (!([acceptedMIMETypes count] == 1 && ![acceptedFileExtensions count]))
     151        return @[CustomFilesTitle, AllFilesTitle];
     152
     153    auto mimeType = [acceptedMIMETypes firstObject];
     154    auto range = [mimeType rangeOfString:@"/"];
     155   
     156    if (!range.length)
     157        return @[CustomFilesTitle, AllFilesTitle];
     158   
     159    auto mimeTypePrefix = [mimeType substringToIndex:range.location];
     160    auto mimeTypeSuffix = [mimeType substringFromIndex:range.location + 1];
     161   
     162    if ([mimeTypeSuffix isEqualToString:@"*"])
     163        return @[[NSString stringWithFormat:@"%@%@ Files", [[mimeTypePrefix substringToIndex:1] uppercaseString], [mimeTypePrefix substringFromIndex:1]], AllFilesTitle];
     164
     165    if ([mimeTypeSuffix length] <= 4)
     166        return @[[NSString stringWithFormat:@"%@ %@", [mimeTypeSuffix uppercaseString], mimeTypePrefix], AllFilesTitle];
     167
     168    return @[[NSString stringWithFormat:@"%@ %@", mimeTypeSuffix, mimeTypePrefix], AllFilesTitle];
     169}
     170
    66171@end
    67172
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKOpenPanelParametersPrivate.h

    r243376 r262895  
    3232@property (nonatomic, readonly, copy) NSArray<NSString *> *_acceptedMIMETypes WK_API_AVAILABLE(macos(10.13.4));
    3333@property (nonatomic, readonly, copy) NSArray<NSString *> *_acceptedFileExtensions WK_API_AVAILABLE(macos(10.13.4));
     34@property (nonatomic, readonly, copy) NSArray<NSString *> *_allowedFileExtensions WK_API_AVAILABLE(macos(10.13.4));
     35@property (nonatomic, readonly, copy) NSArray<NSString *> *_allowedFileExtensionsTitles WK_API_AVAILABLE(macos(10.13.4));
    3436
    3537@end
  • trunk/Tools/ChangeLog

    r262884 r262895  
     12020-06-10  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        [macOS]: The File Picker of the <input> file element should show the selection filter
     4        https://bugs.webkit.org/show_bug.cgi?id=212485
     5
     6        Reviewed by Darin Adler.
     7
     8        Create a view with an NSPopupButton for the file extensions filter. Set
     9        this view as the AccessoryView of the OpenPanel. Create a customized target
     10        for the NSPopupButton to respond to changing its selection.
     11
     12        * MiniBrowser/mac/WK2BrowserWindowController.m:
     13        (-[FileExtensionsPopupTarget initWithOpenPanel:allowedFileExtensions:]):
     14        (-[FileExtensionsPopupTarget popupAction:]):
     15        (-[FileExtensionsPopupTarget dealloc]):
     16        (-[WK2BrowserWindowController dealloc]):
     17        (-[WK2BrowserWindowController createFilterView:popupTarget:]):
     18        (-[WK2BrowserWindowController webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:]):
     19
    1202020-06-10  Jonathan Bedard  <jbedard@apple.com>
    221
  • trunk/Tools/MiniBrowser/mac/WK2BrowserWindowController.m

    r260958 r262895  
    3131#import <WebKit/WKNavigationActionPrivate.h>
    3232#import <WebKit/WKNavigationDelegate.h>
     33#import <WebKit/WKOpenPanelParametersPrivate.h>
    3334#import <WebKit/WKPreferencesPrivate.h>
    3435#import <WebKit/WKUIDelegate.h>
     
    7273@end
    7374
     75// Target for the FileExtensions NSPopupButton in the OpenPanel's AccessoryView.
     76@interface FileExtensionsPopupTarget : NSObject {
     77@private
     78    NSOpenPanel *_openPanel;
     79    NSArray<NSString *> *_allowedFileExtensions;
     80}
     81@end
     82
     83@implementation FileExtensionsPopupTarget
     84
     85- (id)initWithOpenPanel:(NSOpenPanel*)openPanel allowedFileExtensions:(NSArray<NSString *> *)allowedFileExtensions {
     86    if ((self = [super init])) {
     87        _openPanel = openPanel;
     88        _allowedFileExtensions = [allowedFileExtensions copy];
     89    }
     90    return self;
     91}
     92
     93- (void)popupAction:(id)sender {
     94    // Last item.
     95    if ([sender indexOfSelectedItem] == [sender numberOfItems] - 1)
     96        [_openPanel setAllowedFileTypes:nil];
     97    else if (![sender indexOfSelectedItem]) {
     98        // First item.
     99        if (![_allowedFileExtensions count])
     100            [_openPanel setAllowedFileTypes:@[@""]];
     101        else
     102            [_openPanel setAllowedFileTypes:_allowedFileExtensions];
     103    }
     104}
     105
     106- (void)dealloc
     107{
     108    [_allowedFileExtensions release];
     109    [super dealloc];
     110}
     111
     112@end
     113
    74114@interface WK2BrowserWindowController () <NSTextFinderBarContainer, WKNavigationDelegate, WKUIDelegate, _WKIconLoadingDelegate>
    75115@end
     
    86126    NSView *_textFindBarView;
    87127    BOOL _findBarVisible;
     128    FileExtensionsPopupTarget *_fileExtensionsPopupTarget;
    88129}
    89130
     
    171212    [_webView release];
    172213    [_configuration release];
     214    [_fileExtensionsPopupTarget release];
    173215
    174216    [super dealloc];
     
    588630}
    589631
     632- (nullable NSView *)createFilterView:(NSArray *)titles popupTarget:(FileExtensionsPopupTarget*)popupTarget
     633{
     634    NSTextField* label = [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease];
     635    [label setStringValue:@"Format:"];
     636    [label setEditable:NO];
     637    [label setSelectable:NO];
     638    [label setBordered:NO];
     639    [label setBackgroundColor:[NSColor clearColor]];
     640    [label sizeToFit];
     641
     642    NSPopUpButton *button = [[[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO] autorelease];
     643    [button addItemsWithTitles:titles];
     644    [button setTarget:popupTarget];
     645    [button setAction:@selector(popupAction:)];
     646    [button sizeToFit];
     647    [button selectItemAtIndex:0];
     648    [popupTarget popupAction:button];
     649   
     650    NSView* filterView = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
     651    [filterView addSubview:label];
     652    [filterView addSubview:button];
     653
     654    const CGFloat margin = 20;
     655    const CGFloat spacing = 2;
     656    const CGFloat minButtonWidth = 230;
     657
     658    NSRect labelFrame = [label frame];
     659    NSRect buttonFrame = [button frame];
     660    NSRect filterViewFrame = [filterView frame];
     661
     662    // Set a minimum width for 'button'.
     663    buttonFrame.size = NSMakeSize(MAX(NSWidth(buttonFrame), minButtonWidth), NSHeight(buttonFrame));
     664   
     665    // FilterView will lay out 'label' and 'button' horizontally and have a vertical margins.
     666    filterViewFrame.size = NSMakeSize(NSWidth(labelFrame) + NSWidth(buttonFrame) + spacing, MAX(NSHeight(labelFrame), NSHeight(buttonFrame)) + margin * 2);
     667    [filterView setFrame:filterViewFrame];
     668
     669    // 'label' will be laid out center vertically.
     670    labelFrame.origin = NSMakePoint(NSMinX(labelFrame), (NSHeight(filterViewFrame) - NSHeight(labelFrame)) / 2);
     671    [label setFrame:labelFrame];
     672   
     673    // 'button' will be laid out center vertically and it will come after 'label' horizontally.
     674    buttonFrame.origin = NSMakePoint(NSMaxX(labelFrame) + spacing, (NSHeight(filterViewFrame) - NSHeight(buttonFrame)) / 2);
     675    [button setFrame:buttonFrame];
     676
     677    return filterView;
     678}
     679
    590680#if __has_feature(objc_generics)
    591681- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * URLs))completionHandler
     
    597687
    598688    openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
     689
     690    [_fileExtensionsPopupTarget release];
     691    _fileExtensionsPopupTarget = [[FileExtensionsPopupTarget alloc] initWithOpenPanel: openPanel allowedFileExtensions: parameters._allowedFileExtensions];
     692
     693    NSArray *allowedFileExtensionsTitles = parameters._allowedFileExtensionsTitles;
     694    NSView *filterView = [self createFilterView:allowedFileExtensionsTitles popupTarget:_fileExtensionsPopupTarget];
     695    if (filterView)
     696        [openPanel setAccessoryView:filterView];
    599697
    600698    [openPanel beginSheetModalForWindow:webView.window completionHandler:^(NSInteger result) {
Note: See TracChangeset for help on using the changeset viewer.