Title: [272334] trunk
Revision
272334
Author
[email protected]
Date
2021-02-03 12:27:31 -0800 (Wed, 03 Feb 2021)

Log Message

[iOS][FCR] Add new picker for select elements
https://bugs.webkit.org/show_bug.cgi?id=221153
<rdar://problem/73770389>

Reviewed by Tim Horton.

Source/WebKit:

Tapping on a select element should display an context menu that allows
users to choose one of the specified options. Rather than presenting a
UIPickerView, tapping on select elements now create UIContextMenuInteractions,
similar to date and file inputs.

Test: fast/forms/ios/form-control-refresh/select/choose-select-option.html

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _shouldShowAutomaticKeyboardUIIgnoringInputMode]):

The new picker does not bring up the keyboard view on all devices.

(-[WKContentView _elementTypeRequiresAccessoryView:]):

Changed from a static method to an instance method, as the returned
value depends on a flag which is only accessible through the instance.

(-[WKContentView requiresAccessoryView]):
(-[WKContentView _formControlRefreshEnabled]):
(-[WKContentView _shouldShowKeyboardForElement:]):
(-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:]):
(-[WKContentView _removeContextMenuViewIfPossible]):

Do not remove the context menu if an select element is actively being
interacted with.

(-[WKContentView selectControl]):
* UIProcess/ios/forms/WKFormSelectControl.mm:
(-[WKFormSelectControl initWithView:]):
* UIProcess/ios/forms/WKFormSelectPicker.h:
* UIProcess/ios/forms/WKFormSelectPicker.mm:
(-[WKSelectPicker initWithView:]):
(-[WKSelectPicker controlView]):
(-[WKSelectPicker controlBeginEditing]):

Ensure the position information is up-to-date prior to presenting the
context menu.

(-[WKSelectPicker controlEndEditing]):
(-[WKSelectPicker dealloc]):
(-[WKSelectPicker didSelectOptionIndex:]):
(-[WKSelectPicker createMenu]):

Build the menu using UIActions and UIMenus. Since optgroup elements
cannot be nested, only the root UIMenu can contain UIMenus. Submenus
can only contain UIActions.

(-[WKSelectPicker actionForOptionItem:withIndex:]):
(-[WKSelectPicker contextMenuInteraction:previewForHighlightingMenuWithConfiguration:]):
(-[WKSelectPicker _contextMenuInteraction:styleForMenuWithConfiguration:]):
(-[WKSelectPicker contextMenuInteraction:configurationForMenuAtLocation:]):
(-[WKSelectPicker contextMenuInteraction:willDisplayMenuForConfiguration:animator:]):
(-[WKSelectPicker contextMenuInteraction:willEndForConfiguration:animator:]):
(-[WKSelectPicker removeContextMenuInteraction]):
(-[WKSelectPicker ensureContextMenuInteraction]):
(-[WKSelectPicker showSelectPicker]):
(-[WKSelectPicker selectRow:inComponent:extendingSelection:]):

Implement method for testing select pickers.

LayoutTests:

* fast/forms/ios/form-control-refresh/select/choose-select-option-expected.txt: Added.
* fast/forms/ios/form-control-refresh/select/choose-select-option.html: Added.
* resources/ui-helper.js:
(window.UIHelper.waitForContextMenuToShow):

Added a new UIHelper method to wait until a context menu is displayed.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (272333 => 272334)


--- trunk/LayoutTests/ChangeLog	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/LayoutTests/ChangeLog	2021-02-03 20:27:31 UTC (rev 272334)
@@ -1,3 +1,18 @@
+2021-02-03  Aditya Keerthi  <[email protected]>
+
+        [iOS][FCR] Add new picker for select elements
+        https://bugs.webkit.org/show_bug.cgi?id=221153
+        <rdar://problem/73770389>
+
+        Reviewed by Tim Horton.
+
+        * fast/forms/ios/form-control-refresh/select/choose-select-option-expected.txt: Added.
+        * fast/forms/ios/form-control-refresh/select/choose-select-option.html: Added.
+        * resources/ui-helper.js:
+        (window.UIHelper.waitForContextMenuToShow):
+
+        Added a new UIHelper method to wait until a context menu is displayed.
+
 2021-02-03  Youenn Fablet  <[email protected]>
 
         Make sure GPUProcess MediaRecorder handles correctly muted tracks

Added: trunk/LayoutTests/fast/forms/ios/form-control-refresh/select/choose-select-option-expected.txt (0 => 272334)


--- trunk/LayoutTests/fast/forms/ios/form-control-refresh/select/choose-select-option-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/form-control-refresh/select/choose-select-option-expected.txt	2021-02-03 20:27:31 UTC (rev 272334)
@@ -0,0 +1,13 @@
+This test verifies that tapping on a select element and choosing an option via the presented context menu updates the element's value.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS select.value is "January"
+PASS select.value is "April"
+PASS grouped.value is "January"
+PASS grouped.value is "October"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/forms/ios/form-control-refresh/select/choose-select-option.html (0 => 272334)


--- trunk/LayoutTests/fast/forms/ios/form-control-refresh/select/choose-select-option.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/form-control-refresh/select/choose-select-option.html	2021-02-03 20:27:31 UTC (rev 272334)
@@ -0,0 +1,70 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true IOSFormControlRefreshEnabled=true ] -->
+<html>
+    <head>
+        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+        <script src=""
+        <script src=""
+    </head>
+<body>
+<select id="select">
+    <option>January</option>
+    <option>February</option>
+    <option>March</option>
+    <option>April</option>
+    <option>May</option>
+    <option>June</option>
+    <option>July</option>
+    <option>August</option>
+    <option>September</option>
+    <option>October</option>
+    <option>November</option>
+    <option>December</option>
+</select>
+
+<select id="grouped">
+    <optgroup label="Winter">
+        <option>January</option>
+        <option>February</option>
+        <option>March</option>
+    </optgroup>
+    <optgroup label="Spring">
+        <option>April</option>
+        <option>May</option>
+        <option>June</option>
+    </optgroup>
+    <optgroup label="Summer">
+        <option>July</option>
+        <option>August</option>
+        <option>September</option>
+    </optgroup>
+    <optgroup label="Fall">
+        <option>October</option>
+        <option>November</option>
+        <option>December</option>
+    </optgroup>
+</select>
+</body>
+<script>
+jsTestIsAsync = true;
+
+addEventListener("load", async () => {
+    description("This test verifies that tapping on a select element and choosing an option via the presented context menu updates the element's value.");
+
+    shouldBeEqualToString("select.value", "January");
+    await UIHelper.activateElement(select);
+    await UIHelper.waitForContextMenuToShow();
+    await UIHelper.selectFormAccessoryPickerRow(3);
+    await UIHelper.waitForContextMenuToHide();
+    shouldBeEqualToString("select.value", "April");
+
+    shouldBeEqualToString("grouped.value", "January");
+    await UIHelper.activateElement(grouped);
+    await UIHelper.waitForContextMenuToShow();
+    await UIHelper.selectFormAccessoryPickerRow(9);
+    await UIHelper.waitForContextMenuToHide();
+    shouldBeEqualToString("grouped.value", "October");
+
+    finishJSTest();
+});
+</script>
+</html>

Modified: trunk/LayoutTests/resources/ui-helper.js (272333 => 272334)


--- trunk/LayoutTests/resources/ui-helper.js	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/LayoutTests/resources/ui-helper.js	2021-02-03 20:27:31 UTC (rev 272334)
@@ -668,6 +668,22 @@
         });
     }
 
+    static waitForContextMenuToShow()
+    {
+        if (!this.isWebKit2() || !this.isIOSFamily())
+            return Promise.resolve();
+
+        return new Promise(resolve => {
+            testRunner.runUIScript(`
+                (function() {
+                    if (!uiController.isShowingContextMenu)
+                        uiController.didShowContextMenuCallback = () => uiController.uiScriptComplete();
+                    else
+                        uiController.uiScriptComplete();
+                })()`, resolve);
+        });
+    }
+
     static waitForContextMenuToHide()
     {
         if (!this.isWebKit2() || !this.isIOSFamily())

Modified: trunk/Source/WebKit/ChangeLog (272333 => 272334)


--- trunk/Source/WebKit/ChangeLog	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/ChangeLog	2021-02-03 20:27:31 UTC (rev 272334)
@@ -1,3 +1,72 @@
+2021-02-03  Aditya Keerthi  <[email protected]>
+
+        [iOS][FCR] Add new picker for select elements
+        https://bugs.webkit.org/show_bug.cgi?id=221153
+        <rdar://problem/73770389>
+
+        Reviewed by Tim Horton.
+
+        Tapping on a select element should display an context menu that allows
+        users to choose one of the specified options. Rather than presenting a
+        UIPickerView, tapping on select elements now create UIContextMenuInteractions,
+        similar to date and file inputs.
+
+        Test: fast/forms/ios/form-control-refresh/select/choose-select-option.html
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _shouldShowAutomaticKeyboardUIIgnoringInputMode]):
+
+        The new picker does not bring up the keyboard view on all devices.
+
+        (-[WKContentView _elementTypeRequiresAccessoryView:]):
+
+        Changed from a static method to an instance method, as the returned
+        value depends on a flag which is only accessible through the instance.
+
+        (-[WKContentView requiresAccessoryView]):
+        (-[WKContentView _formControlRefreshEnabled]):
+        (-[WKContentView _shouldShowKeyboardForElement:]):
+        (-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:]):
+        (-[WKContentView _removeContextMenuViewIfPossible]):
+
+        Do not remove the context menu if an select element is actively being
+        interacted with.
+
+        (-[WKContentView selectControl]):
+        * UIProcess/ios/forms/WKFormSelectControl.mm:
+        (-[WKFormSelectControl initWithView:]):
+        * UIProcess/ios/forms/WKFormSelectPicker.h:
+        * UIProcess/ios/forms/WKFormSelectPicker.mm:
+        (-[WKSelectPicker initWithView:]):
+        (-[WKSelectPicker controlView]):
+        (-[WKSelectPicker controlBeginEditing]):
+
+        Ensure the position information is up-to-date prior to presenting the
+        context menu.
+
+        (-[WKSelectPicker controlEndEditing]):
+        (-[WKSelectPicker dealloc]):
+        (-[WKSelectPicker didSelectOptionIndex:]):
+        (-[WKSelectPicker createMenu]):
+
+        Build the menu using UIActions and UIMenus. Since optgroup elements
+        cannot be nested, only the root UIMenu can contain UIMenus. Submenus
+        can only contain UIActions.
+
+        (-[WKSelectPicker actionForOptionItem:withIndex:]):
+        (-[WKSelectPicker contextMenuInteraction:previewForHighlightingMenuWithConfiguration:]):
+        (-[WKSelectPicker _contextMenuInteraction:styleForMenuWithConfiguration:]):
+        (-[WKSelectPicker contextMenuInteraction:configurationForMenuAtLocation:]):
+        (-[WKSelectPicker contextMenuInteraction:willDisplayMenuForConfiguration:animator:]):
+        (-[WKSelectPicker contextMenuInteraction:willEndForConfiguration:animator:]):
+        (-[WKSelectPicker removeContextMenuInteraction]):
+        (-[WKSelectPicker ensureContextMenuInteraction]):
+        (-[WKSelectPicker showSelectPicker]):
+        (-[WKSelectPicker selectRow:inComponent:extendingSelection:]):
+
+        Implement method for testing select pickers.
+
 2021-02-03  Per Arne Vollan  <[email protected]>
 
         [macOS] Remove access to graphics related user clients

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (272333 => 272334)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2021-02-03 20:27:31 UTC (rev 272334)
@@ -1258,7 +1258,14 @@
 @end
 #endif // ENABLE(DRAG_SUPPORT)
 
-#if HAVE(LINK_PREVIEW) && USE(UICONTEXTMENU)
+#if USE(UICONTEXTMENU)
+
+@interface UIAction (IPI)
+- (void)_performActionWithSender:(id)sender;
+@end
+
+#if HAVE(LINK_PREVIEW)
+
 @interface UIContextMenuConfiguration (IPI)
 @property (nonatomic, copy) UIContextMenuContentPreviewProvider previewProvider;
 @property (nonatomic, copy) UIContextMenuActionProvider actionProvider;
@@ -1281,8 +1288,10 @@
 @property (nonatomic, strong) _UIClickPresentationInteraction *presentationInteraction;
 @end
 
-#endif // HAVE(LINK_PREVIEW) && USE(UICONTEXTMENU)
+#endif // HAVE(LINK_PREVIEW)
 
+#endif // USE(UICONTEXTMENU)
+
 @interface UIPhysicalKeyboardEvent : UIPressesEvent
 @end
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (272333 => 272334)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-02-03 20:27:31 UTC (rev 272334)
@@ -107,6 +107,7 @@
 @class WKDateTimeInputControl;
 @class WKFocusedFormControlView;
 @class WKFormInputSession;
+@class WKFormSelectControl;
 @class WKHighlightLongPressGestureRecognizer;
 @class WKMouseGestureRecognizer;
 @class WKInspectorNodeSearchGestureRecognizer;
@@ -671,6 +672,10 @@
 - (void)_setMouseEventPolicy:(WebCore::MouseEventPolicy)policy;
 #endif
 
+#if ENABLE(IOS_FORM_CONTROL_REFRESH)
+- (BOOL)_formControlRefreshEnabled;
+#endif
+
 @end
 
 @interface WKContentView (WKTesting)
@@ -692,6 +697,7 @@
 @property (nonatomic, readonly) NSString *selectFormPopoverTitle;
 @property (nonatomic, readonly) NSString *formInputLabel;
 @property (nonatomic, readonly) WKDateTimeInputControl *dateTimeInputControl;
+@property (nonatomic, readonly) WKFormSelectControl *selectControl;
 
 @end
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (272333 => 272334)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-02-03 20:27:31 UTC (rev 272334)
@@ -2025,7 +2025,13 @@
     case WebKit::InputType::DateTimeLocal:
     case WebKit::InputType::Time:
         return NO;
-    case WebKit::InputType::Select:
+    case WebKit::InputType::Select: {
+#if ENABLE(IOS_FORM_CONTROL_REFRESH)
+        if ([self _formControlRefreshEnabled])
+            return NO;
+#endif
+        return !WebKit::currentUserInterfaceIdiomIsPadOrMac();
+    }
 #if ENABLE(INPUT_TYPE_COLOR)
     case WebKit::InputType::Color:
 #endif
@@ -3068,7 +3074,7 @@
 #endif
 }
 
-static bool elementTypeRequiresAccessoryView(WebKit::InputType type)
+- (bool)_elementTypeRequiresAccessoryView:(WebKit::InputType)type
 {
     switch (type) {
     case WebKit::InputType::None:
@@ -3078,6 +3084,13 @@
     case WebKit::InputType::Month:
     case WebKit::InputType::Time:
         return false;
+    case WebKit::InputType::Select: {
+#if ENABLE(IOS_FORM_CONTROL_REFRESH)
+        if ([self _formControlRefreshEnabled])
+            return NO;
+#endif
+        return !WebKit::currentUserInterfaceIdiomIsPadOrMac();
+    }
     case WebKit::InputType::Text:
     case WebKit::InputType::Password:
     case WebKit::InputType::Search:
@@ -3088,7 +3101,6 @@
     case WebKit::InputType::NumberPad:
     case WebKit::InputType::ContentEditable:
     case WebKit::InputType::TextArea:
-    case WebKit::InputType::Select:
     case WebKit::InputType::Week:
 #if ENABLE(INPUT_TYPE_COLOR)
     case WebKit::InputType::Color:
@@ -3105,7 +3117,7 @@
     if ([_formInputSession customInputAccessoryView])
         return YES;
 
-    return elementTypeRequiresAccessoryView(_focusedElementInformation.elementType);
+    return [self _elementTypeRequiresAccessoryView:_focusedElementInformation.elementType];
 }
 
 - (UITextInputAssistantItem *)inputAssistantItem
@@ -5926,6 +5938,18 @@
         [self _updateAccessory];
 }
 
+#if ENABLE(IOS_FORM_CONTROL_REFRESH)
+
+- (BOOL)_formControlRefreshEnabled
+{
+    if (!_page)
+        return NO;
+
+    return _page->preferences().iOSFormControlRefreshEnabled();
+}
+
+#endif
+
 - (const WebKit::FocusedElementInformation&)focusedElementInformation
 {
     return _focusedElementInformation;
@@ -5968,7 +5992,7 @@
     }
 }
 
-static bool shouldShowKeyboardForElement(const WebKit::FocusedElementInformation& information)
+- (bool)_shouldShowKeyboardForElement:(const WebKit::FocusedElementInformation&)information
 {
     if (information.inputMode == WebCore::InputMode::None)
         return false;
@@ -5976,7 +6000,7 @@
     if (mayContainSelectableText(information.elementType))
         return true;
 
-    return elementTypeRequiresAccessoryView(information.elementType);
+    return [self _elementTypeRequiresAccessoryView:information.elementType];
 }
 
 static RetainPtr<NSObject <WKFormPeripheral>> createInputPeripheralWithView(WebKit::InputType type, WKContentView *view)
@@ -6007,7 +6031,7 @@
 - (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode activityStateChanges:(OptionSet<WebCore::ActivityState::Flag>)activityStateChanges userObject:(NSObject <NSSecureCoding> *)userObject
 {
     SetForScope<BOOL> isChangingFocusForScope { _isChangingFocus, self._hasFocusedElement };
-    SetForScope<BOOL> isFocusingElementWithKeyboardForScope { _isFocusingElementWithKeyboard, shouldShowKeyboardForElement(information) };
+    SetForScope<BOOL> isFocusingElementWithKeyboardForScope { _isFocusingElementWithKeyboard, [self _shouldShowKeyboardForElement:information] };
 
     auto inputViewUpdateDeferrer = std::exchange(_inputViewUpdateDeferrer, nullptr);
 
@@ -8012,6 +8036,9 @@
     // and for the date/time picker.
     if ([self dateTimeInputControl])
         return;
+
+    if ([self selectControl])
+        return;
     
     [std::exchange(_contextMenuHintContainerView, nil) removeFromSuperview];
 }
@@ -9191,6 +9218,13 @@
     return nil;
 }
 
+- (WKFormSelectControl *)selectControl
+{
+    if ([_inputPeripheral isKindOfClass:WKFormSelectControl.class])
+        return (WKFormSelectControl *)_inputPeripheral.get();
+    return nil;
+}
+
 - (void)_simulateTextEntered:(NSString *)text
 {
 #if PLATFORM(WATCHOS)

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectControl.mm (272333 => 272334)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectControl.mm	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectControl.mm	2021-02-03 20:27:31 UTC (rev 272334)
@@ -74,6 +74,15 @@
     }
 
     RetainPtr<NSObject <WKFormControl>> control;
+
+#if ENABLE(IOS_FORM_CONTROL_REFRESH)
+    if ([view _formControlRefreshEnabled]) {
+        // FIXME: Add implementation for multi-select picker.
+        control = adoptNS([[WKSelectPicker alloc] initWithView:view]);
+        return [super initWithView:view control:WTFMove(control)];
+    }
+#endif
+
     if (currentUserInterfaceIdiomIsPadOrMac())
         control = adoptNS([[WKSelectPopover alloc] initWithView:view hasGroups:hasGroups]);
     else if (view.focusedElementInformation.isMultiSelect || hasGroups)

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.h (272333 => 272334)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.h	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.h	2021-02-03 20:27:31 UTC (rev 272334)
@@ -38,4 +38,16 @@
 - (instancetype)initWithView:(WKContentView *)view;
 @end
 
+#if ENABLE(IOS_FORM_CONTROL_REFRESH)
+
+@interface WKSelectPicker : NSObject <WKFormControl
+#if USE(UICONTEXTMENU)
+, UIContextMenuInteractionDelegate
+#endif
+>
+- (instancetype)initWithView:(WKContentView *)view;
+@end
+
+#endif
+
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.mm (272333 => 272334)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.mm	2021-02-03 20:16:38 UTC (rev 272333)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.mm	2021-02-03 20:27:31 UTC (rev 272334)
@@ -33,7 +33,9 @@
 #import "WKContentViewInteraction.h"
 #import "WKFormPopover.h"
 #import "WKFormSelectControl.h"
+#import "WKWebViewPrivateForTesting.h"
 #import "WebPageProxy.h"
+#import <WebCore/LocalizedStrings.h>
 
 using namespace WebKit;
 
@@ -464,4 +466,237 @@
 
 @end
 
-#endif  // PLATFORM(IOS_FAMILY)
+#pragma mark - Form Control Refresh
+
+@implementation WKSelectPicker {
+    __weak WKContentView *_view;
+    CGPoint _interactionPoint;
+
+#if USE(UICONTEXTMENU)
+    RetainPtr<UIMenu> _selectMenu;
+    RetainPtr<UIContextMenuInteraction> _selectContextMenuInteraction;
+#endif
+}
+
+- (instancetype)initWithView:(WKContentView *)view
+{
+    if (!(self = [super init]))
+        return nil;
+
+    _view = view;
+    _interactionPoint = [_view lastInteractionLocation];
+#if USE(UICONTEXTMENU)
+    _selectMenu = [self createMenu];
+#endif
+
+    return self;
+}
+
+- (UIView *)controlView
+{
+    return nil;
+}
+
+- (void)controlBeginEditing
+{
+    [_view startRelinquishingFirstResponderToFocusedElement];
+
+#if USE(UICONTEXTMENU)
+    WebKit::InteractionInformationRequest positionInformationRequest { WebCore::IntPoint(_view.focusedElementInformation.lastInteractionLocation) };
+    [_view doAfterPositionInformationUpdate:^(WebKit::InteractionInformationAtPosition interactionInformation) {
+        [self showSelectPicker];
+    } forRequest:positionInformationRequest];
+#endif
+}
+
+- (void)controlEndEditing
+{
+    [_view stopRelinquishingFirstResponderToFocusedElement];
+
+#if USE(UICONTEXTMENU)
+    [self removeContextMenuInteraction];
+#endif
+}
+
+- (void)dealloc
+{
+#if USE(UICONTEXTMENU)
+    [self removeContextMenuInteraction];
+#endif
+    [super dealloc];
+}
+
+- (void)didSelectOptionIndex:(NSInteger)index
+{
+    [_view page]->setFocusedElementSelectedIndex(index);
+}
+
+#if USE(UICONTEXTMENU)
+
+- (UIMenu *)createMenu
+{
+    if (!_view.focusedSelectElementOptions.size()) {
+        UIAction *emptyAction = [UIAction actionWithTitle:WEB_UI_STRING_KEY("No Options", "No Options Select Popover", "Empty select list") image:nil identifier:nil handler:^(__kindof UIAction *action) { }];
+        emptyAction.attributes = UIMenuElementAttributesDisabled;
+        return [UIMenu menuWithTitle:@"" children:@[emptyAction]];
+    }
+
+    NSMutableArray *items = [NSMutableArray array];
+    NSInteger optionIndex = 0;
+
+    size_t currentIndex = 0;
+    while (currentIndex < _view.focusedSelectElementOptions.size()) {
+        auto& optionItem = _view.focusedSelectElementOptions[currentIndex];
+        if (optionItem.isGroup) {
+            NSString *groupText = optionItem.text;
+            NSMutableArray *groupedItems = [NSMutableArray array];
+
+            currentIndex++;
+            while (currentIndex < _view.focusedSelectElementOptions.size()) {
+                optionItem = _view.focusedSelectElementOptions[currentIndex];
+                if (optionItem.isGroup)
+                    break;
+
+                UIAction *action = "" actionForOptionItem:optionItem withIndex:optionIndex];
+                [groupedItems addObject:action];
+                optionIndex++;
+                currentIndex++;
+            }
+
+            UIMenu *groupMenu = [UIMenu menuWithTitle:groupText children:groupedItems];
+            [items addObject:groupMenu];
+            continue;
+        }
+
+        UIAction *action = "" actionForOptionItem:optionItem withIndex:optionIndex];
+        [items addObject:action];
+        optionIndex++;
+        currentIndex++;
+    }
+
+    return [UIMenu menuWithTitle:@"" children:items];
+}
+
+- (UIAction *)actionForOptionItem:(const OptionItem&)option withIndex:(NSInteger)optionIndex
+{
+    UIAction *optionAction = [UIAction actionWithTitle:option.text image:nil identifier:nil handler:^(__kindof UIAction *action) {
+        [self didSelectOptionIndex:optionIndex];
+    }];
+
+    if (option.disabled)
+        optionAction.attributes = UIMenuElementAttributesDisabled;
+
+    if (option.isSelected)
+        optionAction.state = UIMenuElementStateOn;
+
+    return optionAction;
+}
+
+- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction previewForHighlightingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
+{
+    return [_view _createTargetedContextMenuHintPreviewIfPossible];
+}
+
+- (_UIContextMenuStyle *)_contextMenuInteraction:(UIContextMenuInteraction *)interaction styleForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
+{
+    _UIContextMenuStyle *style = [_UIContextMenuStyle defaultStyle];
+    style.preferredLayout = _UIContextMenuLayoutCompactMenu;
+    return style;
+}
+
+- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location
+{
+    UIContextMenuActionProvider actionMenuProvider = [weakSelf = WeakObjCPtr<WKSelectPicker>(self)] (NSArray<UIMenuElement *> *) -> UIMenu * {
+        auto strongSelf = weakSelf.get();
+        if (!strongSelf)
+            return nil;
+
+        return strongSelf->_selectMenu.get();
+    };
+
+    return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:actionMenuProvider];
+}
+
+- (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willDisplayMenuForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id <UIContextMenuInteractionAnimating>)animator
+{
+    [animator addCompletion:[weakSelf = WeakObjCPtr<WKSelectPicker>(self)] {
+        auto strongSelf = weakSelf.get();
+        if (strongSelf)
+            [strongSelf->_view.webView _didShowContextMenu];
+    }];
+}
+
+- (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willEndForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id <UIContextMenuInteractionAnimating>)animator
+{
+    [animator addCompletion:[weakSelf = WeakObjCPtr<WKSelectPicker>(self)] {
+        auto strongSelf = weakSelf.get();
+        if (strongSelf) {
+            [strongSelf->_view accessoryDone];
+            [strongSelf->_view.webView _didDismissContextMenu];
+        }
+    }];
+}
+
+- (void)removeContextMenuInteraction
+{
+    if (!_selectContextMenuInteraction)
+        return;
+
+    [_view removeInteraction:_selectContextMenuInteraction.get()];
+    _selectContextMenuInteraction = nil;
+    [_view _removeContextMenuViewIfPossible];
+    [_view.webView _didDismissContextMenu];
+}
+
+- (void)ensureContextMenuInteraction
+{
+    if (_selectContextMenuInteraction)
+        return;
+
+    _selectContextMenuInteraction = adoptNS([[UIContextMenuInteraction alloc] initWithDelegate:self]);
+    [_view addInteraction:_selectContextMenuInteraction.get()];
+}
+
+- (void)showSelectPicker
+{
+    [self ensureContextMenuInteraction];
+    [_selectContextMenuInteraction _presentMenuAtLocation:_interactionPoint];
+}
+
+#endif // USE(UICONTEXTMENU)
+
+// WKSelectTesting
+- (void)selectRow:(NSInteger)rowIndex inComponent:(NSInteger)componentIndex extendingSelection:(BOOL)extendingSelection
+{
+#if USE(UICONTEXTMENU)
+    NSInteger currentRow = 0;
+
+    NSArray<UIMenuElement *> *menuElements = [_selectMenu children];
+    for (UIMenuElement *menuElement in menuElements) {
+        if ([menuElement isKindOfClass:UIAction.class]) {
+            if (currentRow == rowIndex) {
+                [(UIAction *)menuElement _performActionWithSender:nil];
+                break;
+            }
+
+            currentRow++;
+            continue;
+        }
+
+        UIMenu *groupedMenu = (UIMenu *)menuElement;
+        if (currentRow + groupedMenu.children.count <= (NSUInteger)rowIndex)
+            currentRow += groupedMenu.children.count;
+        else {
+            UIAction *action = "" *)[groupedMenu.children objectAtIndex:rowIndex - currentRow];
+            [action _performActionWithSender:nil];
+            break;
+        }
+    }
+
+    [self removeContextMenuInteraction];
+#endif
+}
+
+@end
+
+#endif // PLATFORM(IOS_FAMILY)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to