Skip to content
Permalink
Browse files
Adopt UIContextMenu for WKFileUploadPanel
https://bugs.webkit.org/show_bug.cgi?id=208687

Reviewed by Tim Horton.

Source/WebCore:

New string, no tests needed.

* en.lproj/Localizable.strings:

Source/WebKit:

Move from the depricated UIDocumentMenuViewController to UIContextMenu.
UI change, not currently testable.

* Platform/spi/ios/UIKitSPI.h:
* UIProcess/ios/forms/WKFileUploadPanel.mm:
(-[WKFileUploadPanel dealloc]):
(-[WKFileUploadPanel presentWithParameters:resultListener:]):
(-[WKFileUploadPanel dismiss]):
(-[WKFileUploadPanel _browseFilesButtonLabel]):
(-[WKFileUploadPanel contextMenuInteraction:previewForHighlightingMenuWithConfiguration:]):
(-[WKFileUploadPanel _contextMenuInteraction:styleForMenuWithConfiguration:]):
(-[WKFileUploadPanel contextMenuInteraction:configurationForMenuAtLocation:]):
(-[WKFileUploadPanel contextMenuInteraction:willEndForConfiguration:animator:]):
(-[WKFileUploadPanel _removeInteraction]):
(-[WKFileUploadPanel _initInteraction]):
(-[WKFileUploadPanel _showFilePickerMenu]):
(-[WKFileUploadPanel _showDocumentPickerMenu]):
(-[WKFileUploadPanel _presentPopoverWithContentViewController:animated:]):
(-[WKFileUploadPanel _presentFullscreenViewController:animated:]):
(photoLibraryIcon): Deleted.
(cameraIcon): Deleted.
(-[WKFileUploadPanel documentMenu:didPickDocumentPicker:]): Deleted.
(-[WKFileUploadPanel documentMenuWasCancelled:]): Deleted.


Canonical link: https://commits.webkit.org/221701@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@258092 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
megangardner committed Mar 8, 2020
1 parent 2cd1bf7 commit ee02a2ae28ea6d3849afb527dfca9967d0afd0e7
Showing 5 changed files with 168 additions and 66 deletions.
@@ -1,3 +1,14 @@
2020-03-07 Megan Gardner <megan_gardner@apple.com>

Adopt UIContextMenu for WKFileUploadPanel
https://bugs.webkit.org/show_bug.cgi?id=208687

Reviewed by Tim Horton.

New string, no tests needed.

* en.lproj/Localizable.strings:

2020-03-07 Daniel Bates <dabates@apple.com>

[iOS] Implement support for dictation alternatives
@@ -154,6 +154,9 @@
/* Label for the book with Apple Pay button. */
"Book with Apple Pay" = "Book with Apple Pay";

/* File Upload alert sheet button string for choosing an existing file from the file brower */
"Browse (file upload action sheet)" = "Browse";

/* Option in segmented control for inserting a bulleted list in text editing */
"Bulleted list" = "Bulleted list";

@@ -1,3 +1,34 @@
2020-03-07 Megan Gardner <megan_gardner@apple.com>

Adopt UIContextMenu for WKFileUploadPanel
https://bugs.webkit.org/show_bug.cgi?id=208687

Reviewed by Tim Horton.

Move from the depricated UIDocumentMenuViewController to UIContextMenu.
UI change, not currently testable.

* Platform/spi/ios/UIKitSPI.h:
* UIProcess/ios/forms/WKFileUploadPanel.mm:
(-[WKFileUploadPanel dealloc]):
(-[WKFileUploadPanel presentWithParameters:resultListener:]):
(-[WKFileUploadPanel dismiss]):
(-[WKFileUploadPanel _browseFilesButtonLabel]):
(-[WKFileUploadPanel contextMenuInteraction:previewForHighlightingMenuWithConfiguration:]):
(-[WKFileUploadPanel _contextMenuInteraction:styleForMenuWithConfiguration:]):
(-[WKFileUploadPanel contextMenuInteraction:configurationForMenuAtLocation:]):
(-[WKFileUploadPanel contextMenuInteraction:willEndForConfiguration:animator:]):
(-[WKFileUploadPanel _removeInteraction]):
(-[WKFileUploadPanel _initInteraction]):
(-[WKFileUploadPanel _showFilePickerMenu]):
(-[WKFileUploadPanel _showDocumentPickerMenu]):
(-[WKFileUploadPanel _presentPopoverWithContentViewController:animated:]):
(-[WKFileUploadPanel _presentFullscreenViewController:animated:]):
(photoLibraryIcon): Deleted.
(cameraIcon): Deleted.
(-[WKFileUploadPanel documentMenu:didPickDocumentPicker:]): Deleted.
(-[WKFileUploadPanel documentMenuWasCancelled:]): Deleted.

2020-03-07 Peng Liu <peng.liu6@apple.com>

Use the feature flags mechanism to give default feature preference values
@@ -1098,13 +1098,25 @@ typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {
@property (nonatomic, strong) UIImage *image;
@end

typedef NS_ENUM(NSUInteger, _UIContextMenuLayout) {
_UIContextMenuLayoutActionsOnly = 1,
_UIContextMenuLayoutCompactMenu = 3,
};

@interface _UIContextMenuStyle : NSObject <NSCopying>
@property (nonatomic) _UIContextMenuLayout preferredLayout;
+ (instancetype)defaultStyle;
@end

#if USE(UICONTEXTMENU)
@interface UITargetedPreview ()
@property (nonatomic, strong, setter=_setOverridePositionTrackingView:) UIView *overridePositionTrackingView;
@end

@interface UIContextMenuInteraction ()
- (void)_presentMenuAtLocation:(CGPoint)location;
@end

#endif // USE(UICONTEXTMENU)

#if HAVE(LINK_PREVIEW) && USE(UICONTEXTMENU)
@@ -47,6 +47,7 @@
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/UTIUtilities.h>
#import <wtf/RetainPtr.h>
#import <wtf/WeakObjCPtr.h>
#import <wtf/text/StringView.h>

using namespace WebKit;
@@ -67,18 +68,6 @@ static bool arrayContainsUTIThatConformsTo(NSArray<NSString *> *typeIdentifiers,
return false;
}

#pragma mark - Document picker icons

static inline UIImage *photoLibraryIcon()
{
return _UIImageGetWebKitPhotoLibraryIcon();
}

static inline UIImage *cameraIcon()
{
return _UIImageGetWebKitTakePhotoOrVideoIcon();
}

#pragma mark - _WKFileUploadItem

@interface _WKFileUploadItem : NSObject
@@ -161,11 +150,11 @@ - (UIImage *)displayImage

#pragma mark - WKFileUploadPanel

@interface WKFileUploadPanel () <UIPopoverControllerDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIDocumentPickerDelegate, UIDocumentMenuDelegate>
@interface WKFileUploadPanel () <UIPopoverControllerDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIDocumentPickerDelegate, UIContextMenuInteractionDelegate>
@end

@implementation WKFileUploadPanel {
WKContentView *_view;
WeakObjCPtr<WKContentView> _view;
RefPtr<WebKit::WebOpenPanelResultListenerProxy> _listener;
RetainPtr<NSArray> _mimeTypes;
CGPoint _interactionPoint;
@@ -176,7 +165,7 @@ @implementation WKFileUploadPanel {
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
RetainPtr<UIPopoverController> _presentationPopover; // iPad for action sheet and Photo Library.
ALLOW_DEPRECATED_DECLARATIONS_END
RetainPtr<UIDocumentMenuViewController> _documentMenuController;
RetainPtr<UIContextMenuInteraction> _documentContextMenuInteraction;
RetainPtr<UIDocumentPickerViewController> _documentPickerController;
WebCore::MediaCaptureType _mediaCaptureType;
}
@@ -193,8 +182,8 @@ - (void)dealloc
{
[_imagePicker setDelegate:nil];
[_presentationPopover setDelegate:nil];
[_documentMenuController setDelegate:nil];
[_documentPickerController setDelegate:nil];
[self removeContextMenuInteraction];

[super dealloc];
}
@@ -278,15 +267,15 @@ - (void)presentWithParameters:(API::OpenPanelParameters*)parameters resultListen
return;
}

[self _showDocumentPickerMenu];
[self showDocumentPickerMenu];
}

- (void)dismiss
{
// Dismiss any view controller that is being presented. This works for all types of view controllers, popovers, etc.
// If there is any kind of view controller presented on this view, it will be removed.

[[UIViewController _viewControllerForFullScreenPresentationFromView:_view] dismissViewControllerAnimated:NO completion:nil];
[[UIViewController _viewControllerForFullScreenPresentationFromView:_view.getAutoreleased()] dismissViewControllerAnimated:NO completion:nil];

[_presentationPopover setDelegate:nil];
_presentationPopover = nil;
@@ -318,13 +307,13 @@ - (void)_dismissDisplayAnimated:(BOOL)animated
NSMutableArray<NSString *> *actionTitles = [NSMutableArray array];

NSArray *mediaTypes = UTIsForMIMETypes(_mimeTypes.get()).allObjects;
BOOL containsImageMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeImage);
BOOL containsVideoMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeMovie);
if (containsImageMediaType || containsVideoMediaType) {
BOOL allowsImageMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeImage);
BOOL allowsVideoMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeMovie);
if (allowsImageMediaType || allowsVideoMediaType) {
[actionTitles addObject:@"Photo Library"];
if (containsImageMediaType && containsVideoMediaType)
if (allowsImageMediaType && allowsVideoMediaType)
[actionTitles addObject:@"Take Photo or Video"];
else if (containsVideoMediaType)
else if (allowsVideoMediaType)
[actionTitles addObject:@"Take Video"];
else
[actionTitles addObject:@"Take Photo"];
@@ -386,6 +375,11 @@ - (void)_dismissDisplayAnimated:(BOOL)animated

#pragma mark - Source selection menu

- (NSString *)_browseFilesButtonLabel
{
return WEB_UI_STRING_KEY("Browse", "Browse (file upload action sheet)", "File Upload alert sheet button string for choosing an existing file from the file brower");
}

- (NSString *)_photoLibraryButtonLabel
{
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");
@@ -403,48 +397,113 @@ - (NSString *)_cameraButtonLabelAllowingPhoto:(BOOL)allowPhoto allowingVideo:(BO
return WEB_UI_STRING_KEY("Take Photo", "Take Photo (file upload action sheet)", "File Upload alert sheet camera button string for taking only photos");
}

- (void)_showDocumentPickerMenu
- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction previewForHighlightingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
{
NSArray *mediaTypes = UTIsForMIMETypes(_mimeTypes.get()).allObjects;
RetainPtr<UIPreviewParameters> unusedPreviewParameters = adoptNS([[UIPreviewParameters alloc] init]);
RetainPtr<UIPreviewTarget> previewTarget = adoptNS([[UIPreviewTarget alloc] initWithContainer:_view.getAutoreleased() center:CGPointMake(_interactionPoint.x, _interactionPoint.y)]);
RetainPtr<UITargetedPreview> preview = adoptNS([[UITargetedPreview alloc] initWithView:_view.getAutoreleased() parameters:unusedPreviewParameters.get() target:previewTarget.get()]);

BOOL containsImageMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeImage);
BOOL containsVideoMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeMovie);
return preview.autorelease();
}

#if PLATFORM(MACCATALYST)
// FIXME 49961589: Support picking media with UIImagePickerController
BOOL shouldPresentDocumentMenuViewController = NO;
#else
BOOL shouldPresentDocumentMenuViewController = containsImageMediaType || containsVideoMediaType;
#endif
- (_UIContextMenuStyle *)_contextMenuInteraction:(UIContextMenuInteraction *)interaction styleForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
{
_UIContextMenuStyle *style = [_UIContextMenuStyle defaultStyle];
style.preferredLayout = _UIContextMenuLayoutCompactMenu;
return style;
}

NSArray *documentTypes = mediaTypes.count ? mediaTypes : @[(__bridge NSString *)kUTTypeItem];
if (shouldPresentDocumentMenuViewController) {
// FIXME 49979442: UIDocumentMenuViewController is deprecated. Use UIDocumentPickerViewController instead to support multiple file selection.
_documentMenuController = adoptNS([[UIDocumentMenuViewController alloc] _initIgnoringApplicationEntitlementForImportOfTypes:documentTypes]);
[_documentMenuController setDelegate:self];
- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location {

UIContextMenuActionProvider actionMenuProvider = [self, weakSelf = WeakObjCPtr<WKFileUploadPanel>(self)] (NSArray<UIMenuElement *> *) -> UIMenu * {
NSArray *actions;
NSArray *mediaTypes = UTIsForMIMETypes(_mimeTypes.get()).allObjects;

BOOL allowsImageMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeImage);
BOOL allowsVideoMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeMovie);
auto strongSelf = weakSelf.get();

if (!strongSelf)
return nil;

UIAction *browseAction = [UIAction actionWithTitle:[strongSelf _browseFilesButtonLabel] image:[UIImage systemImageNamed:@"ellipsis"] identifier:@"browse" handler:^(__kindof UIAction *action) {
[self showFilePickerMenu];
}];

[_documentMenuController addOptionWithTitle:[self _photoLibraryButtonLabel] image:photoLibraryIcon() order:UIDocumentMenuOrderFirst handler:^{
UIAction *photoAction = [UIAction actionWithTitle:[strongSelf _photoLibraryButtonLabel] image:[UIImage systemImageNamed:@"rectangle.on.rectangle"] identifier:@"photo" handler:^(__kindof UIAction *action) {
[self _showPhotoPickerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}];

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSString *cameraString = [self _cameraButtonLabelAllowingPhoto:containsImageMediaType allowingVideo:containsVideoMediaType];
[_documentMenuController addOptionWithTitle:cameraString image:cameraIcon() order:UIDocumentMenuOrderFirst handler:^{
NSString *cameraString = [strongSelf _cameraButtonLabelAllowingPhoto:allowsImageMediaType allowingVideo:allowsVideoMediaType];
UIAction *cameraAction = [UIAction actionWithTitle:cameraString image:[UIImage systemImageNamed:@"camera.fill"] identifier:@"camera" handler:^(__kindof UIAction *action) {
_usingCamera = YES;
[self _showPhotoPickerWithSourceType:UIImagePickerControllerSourceTypeCamera];
}];
}
actions = @[photoAction, cameraAction, browseAction];
} else
actions = @[photoAction, browseAction];

return [UIMenu menuWithTitle:@"" children:actions];
};

return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:actionMenuProvider];
}

- (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willEndForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id<UIContextMenuInteractionAnimating>)animator
{
[animator addCompletion:^{
[self removeContextMenuInteraction];
[self _cancel];
}];
}

[self _presentMenuOptionForCurrentInterfaceIdiom:_documentMenuController.get()];
} else {
// Image and Video types are not accepted so bypass the menu and open the file picker directly.
_documentPickerController = adoptNS([[UIDocumentPickerViewController alloc] initWithDocumentTypes:documentTypes inMode:UIDocumentPickerModeImport]);
[_documentPickerController setAllowsMultipleSelection:_allowMultipleFiles];
[_documentPickerController setDelegate:self];
[self _presentFullscreenViewController:_documentPickerController.get() animated:YES];
- (void)removeContextMenuInteraction
{
if (_documentContextMenuInteraction) {
[_view removeInteraction:_documentContextMenuInteraction.get()];
_documentContextMenuInteraction = nil;
}
}

// Clear out the view controller we just presented. Don't save a reference to the UIDocumentMenuViewController / UIDocumentPickerViewController as it is self dismissing.
- (void)ensureContextMenuInteraction
{
if (!_documentContextMenuInteraction) {
_documentContextMenuInteraction = adoptNS([[UIContextMenuInteraction alloc] initWithDelegate:self]);
[_view addInteraction:_documentContextMenuInteraction.get()];
}
}

- (void)showFilePickerMenu
{
NSArray *mediaTypes = UTIsForMIMETypes(_mimeTypes.get()).allObjects;
NSArray *documentTypes = mediaTypes.count ? mediaTypes : @[(__bridge NSString *)kUTTypeItem];

_documentPickerController = adoptNS([[UIDocumentPickerViewController alloc] initWithDocumentTypes:documentTypes inMode:UIDocumentPickerModeImport]);
[_documentPickerController setAllowsMultipleSelection:_allowMultipleFiles];
[_documentPickerController setDelegate:self];
[self _presentFullscreenViewController:_documentPickerController.get() animated:YES];
}

- (void)showDocumentPickerMenu
{
NSArray *mediaTypes = UTIsForMIMETypes(_mimeTypes.get()).allObjects;

#if PLATFORM(MACCATALYST)
// FIXME 49961589: Support picking media with UIImagePickerController
BOOL shouldPresentDocumentMenuViewController = NO;
#else
BOOL allowsImageMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeImage);
BOOL allowsVideoMediaType = !mediaTypes.count || arrayContainsUTIThatConformsTo(mediaTypes, kUTTypeMovie);
BOOL shouldPresentDocumentMenuViewController = allowsImageMediaType || allowsVideoMediaType;
#endif
if (shouldPresentDocumentMenuViewController) {
[self ensureContextMenuInteraction];
[_documentContextMenuInteraction _presentMenuAtLocation:CGPointMake(_interactionPoint.x, _interactionPoint.y)];
} else // Image and Video types are not accepted so bypass the menu and open the file picker directly.
[self showFilePickerMenu];

// Clear out the view controller we just presented. Don't save a reference to the UIDocumentPickerViewController as it is self dismissing.
_presentationViewController = nil;
}

@@ -520,14 +579,14 @@ - (void)_presentPopoverWithContentViewController:(UIViewController *)contentView
_presentationPopover = adoptNS([[UIPopoverController alloc] initWithContentViewController:contentViewController]);
ALLOW_DEPRECATED_DECLARATIONS_END
[_presentationPopover setDelegate:self];
[_presentationPopover presentPopoverFromRect:CGRectIntegral(CGRectMake(_interactionPoint.x, _interactionPoint.y, 1, 1)) inView:_view permittedArrowDirections:UIPopoverArrowDirectionAny animated:animated];
[_presentationPopover presentPopoverFromRect:CGRectIntegral(CGRectMake(_interactionPoint.x, _interactionPoint.y, 1, 1)) inView:_view.getAutoreleased() permittedArrowDirections:UIPopoverArrowDirectionAny animated:animated];
}

- (void)_presentFullscreenViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[self _dismissDisplayAnimated:animated];

_presentationViewController = [UIViewController _viewControllerForFullScreenPresentationFromView:_view];
_presentationViewController = [UIViewController _viewControllerForFullScreenPresentationFromView:_view.getAutoreleased()];
[_presentationViewController presentViewController:viewController animated:animated completion:nil];
}

@@ -540,20 +599,6 @@ - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverControl
[self _cancel];
}

#pragma mark - UIDocumentMenuDelegate implementation

- (void)documentMenu:(UIDocumentMenuViewController *)documentMenu didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker
{
documentPicker.delegate = self;
[self _presentFullscreenViewController:documentPicker animated:YES];
}

- (void)documentMenuWasCancelled:(UIDocumentMenuViewController *)documentMenu
{
[self _dismissDisplayAnimated:YES];
[self _cancel];
}

#pragma mark - UIDocumentPickerControllerDelegate implementation

static NSString *displayStringForDocumentsAtURLs(NSArray<NSURL *> *urls)

0 comments on commit ee02a2a

Please sign in to comment.