[iOS] [Datalist] Can't pick datalist suggestions in a stock WKWebView
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Oct 2018 22:00:32 +0000 (22:00 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Oct 2018 22:00:32 +0000 (22:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190621
<rdar://problem/45310649>

Reviewed by Tim Horton.

Source/WebKit:

Fixes the bug by refactoring datalist suggestion information on iOS; currently, we override text suggestions on
_WKFormInputSession. This only works for a few internal clients (including Safari) that set a _WKInputDelegate
and also implement either -_webView:willStartInputSession: or -_webView:didStartInputSession:, which is
necessary in order to ensure that WebKit creates and maintains a form input session.

The two pieces of information that datalist code needs to vend to WKContentView are a list of UITextSuggestions
and a custom input view, which are both currently properties of _WKFormInputSession. This patch lifts these out
of the input session and makes them properties of WKContentView, which are used in
WebDataListSuggestionsDropdownIOS.

Test: fast/forms/datalist/datalist-textinput-suggestions-order.html

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:

Add new properties to WKContentView: an input view for datalist suggestions, and a list of text suggestions.

(-[WKFormInputSession setSuggestions:]):
(-[WKContentView setupInteraction]):
(-[WKContentView cleanupInteraction]):
(-[WKContentView _endEditing]):

Pull out common logic when resigning first responder or tabbing to the next or previous text field into a new
helper. This helper notifies `_inputPeripheral`, `_formInputSession`, and `_dataListTextSuggestionsInputView`
when editing has ended; the input peripheral and suggestions input view use this chance to send the value of the
form control to the web process.

(-[WKContentView resignFirstResponderForWebView]):
(-[WKContentView inputView]):

If a custom input view is not set but we have an input view for a datalist's text suggestions, use the datalist
input view.

(-[WKContentView accessoryTab:]):
(-[WKContentView _stopAssistingNode]):

Clear datalist state on WKContentView.

(-[WKContentView dataListTextSuggestionsInputView]):
(-[WKContentView dataListTextSuggestions]):
(-[WKContentView setDataListTextSuggestionsInputView:]):
(-[WKContentView setDataListTextSuggestions:]):
(-[WKContentView updateTextSuggestionsForInputDelegate]):

Pull out logic for setting suggestions on UIKit's `inputDelegate` (i.e. UIKeyboardImpl). We now first consult
internally-vended text suggestions from _WKFormInputSession; if an internal client has not overridden our text
suggestions, then we simply use suggestions from the current datalist (if present).

* UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm:
(-[WKDataListSuggestionsPicker updateWithInformation:]):
(-[WKDataListSuggestionsPicker showSuggestionsDropdown:activationType:]):
(-[WKDataListSuggestionsPicker invalidate]):
(-[WKDataListSuggestionsPopover updateWithInformation:]):
(-[WKDataListSuggestionsPopover showSuggestionsDropdown:activationType:]):
(-[WKDataListSuggestionsPopover didSelectOptionAtIndex:]):

Change all the places that currently manipulate WKContentView's form input session to directly set text
suggestions and the text suggestion input view on the content view instead.

Tools:

Add a UIScriptController hook to resign first responder on WKWebView. See LayoutTests/ChangeLog for more detail.

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::resignFirstResponder):
* DumpRenderTree/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::resignFirstResponder):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::resignFirstResponder):
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/UIScriptControllerCocoa.mm:
(WTR::UIScriptController::resignFirstResponder):
* WebKitTestRunner/ios/PlatformWebViewIOS.mm:
(WTR::PlatformWebView::makeWebViewFirstResponder):

Implement this method stub on iOS, to ensure that TestController::resetStateToConsistentValues restores first
responder on the WKWebView when running iOS layout tests.

* WebKitTestRunner/ios/TestControllerIOS.mm:
(WTR::TestController::platformResetStateToConsistentValues):

After resigning first responder to dismiss any on-screen keyboard, ensure that we restore first responder.

LayoutTests:

Refactor an existing layout test to run on both iOS and macOS. On both platforms, it checks that the top
suggestion respects option element order in the document, as well as the current contents of the text field.
On macOS, we use arrow keys and hit return to select a suggestion; on iOS, we tap the suggestions button and
simulate hitting the done button on the input view to dismiss the keyboard.

* fast/forms/datalist/datalist-textinput-suggestions-order-expected.txt:
* fast/forms/datalist/datalist-textinput-suggestions-order.html:
* platform/ios/TestExpectations:

Enable this test on iOS.

* resources/ui-helper.js:
(window.UIHelper.resignFirstResponder):
(window.UIHelper):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237305 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/datalist/datalist-textinput-suggestions-order-expected.txt
LayoutTests/fast/forms/datalist/datalist-textinput-suggestions-order.html
LayoutTests/platform/ios/TestExpectations
LayoutTests/resources/ui-helper.js
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm
Tools/ChangeLog
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/DumpRenderTree/mac/UIScriptControllerMac.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/UIScriptControllerCocoa.mm
Tools/WebKitTestRunner/ios/PlatformWebViewIOS.mm
Tools/WebKitTestRunner/ios/TestControllerIOS.mm

index 9608469..93a2ab8 100644 (file)
@@ -1,3 +1,26 @@
+2018-10-19  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] [Datalist] Can't pick datalist suggestions in a stock WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=190621
+        <rdar://problem/45310649>
+
+        Reviewed by Tim Horton.
+
+        Refactor an existing layout test to run on both iOS and macOS. On both platforms, it checks that the top
+        suggestion respects option element order in the document, as well as the current contents of the text field.
+        On macOS, we use arrow keys and hit return to select a suggestion; on iOS, we tap the suggestions button and
+        simulate hitting the done button on the input view to dismiss the keyboard.
+
+        * fast/forms/datalist/datalist-textinput-suggestions-order-expected.txt:
+        * fast/forms/datalist/datalist-textinput-suggestions-order.html:
+        * platform/ios/TestExpectations:
+
+        Enable this test on iOS.
+
+        * resources/ui-helper.js:
+        (window.UIHelper.resignFirstResponder):
+        (window.UIHelper):
+
 2018-10-19  John Wilander  <wilander@apple.com>
 
         Only cap lifetime of persistent cookies created client-side through document.cookie when resource load statistics is enabled
index ead9e5c..2ad5726 100644 (file)
@@ -1,11 +1,10 @@
-Test to verify that prefix-matched values are displayed first
 
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS input.value is "Orange"
-PASS input.value is "Apple"
-PASS successfullyParsed is true
-
-TEST COMPLETE
+Top suggestion with empty text field: "Orange"
+Top suggestion with "a" in text field: "Apple"
 
+This test verifies that datalist suggestions in a text field are ordered in a way that respects the order of option elements in the document, as well as the contents of the text field. To test manually:
+Focus the text field.
+Check that "Orange" is the top suggestion.
+Type "a" and blur the text field.
+Focus the text field again.
+Check that "Apple" is the top suggestion.
index 4aeee8c..60f1640 100644 (file)
@@ -1,8 +1,18 @@
-<!DOCTYPE html>
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
 <html>
 <head>
-<script src="../../../resources/js-test-pre.js"></script>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
 <script src="../../../resources/ui-helper.js"></script>
+<style>
+input {
+    width: 300px;
+    height: 50px;
+}
+
+body {
+    margin: 0;
+}
+</style>
 </head>
 <body>
 
     <option>Pear</option>
     <option>Apple</option>
 </datalist>
+<pre>Top suggestion with empty text field: "<span id="first"></span>"</pre>
+<pre>Top suggestion with "a" in text field: "<span id="second"></span>"</pre>
+<br>
+<div>This test verifies that datalist suggestions in a text field are ordered in a way that respects the order of option
+elements in the document, as well as the contents of the text field. To test manually:</div>
+<ol>
+    <li>Focus the text field.</li>
+    <li>Check that "Orange" is the top suggestion.</li>
+    <li>Type "a" and blur the text field.</li>
+    <li>Focus the text field again.</li>
+    <li>Check that "Apple" is the top suggestion.</li>
+</ol>
 
 <script>
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
 
-description('Test to verify that prefix-matched values are displayed first');
-
-var input = document.getElementById("fruit");
+function focusInputFieldAndChooseFirstSuggestion()
+{
+    return new Promise(async resolve => {
+        await UIHelper.activateAndWaitForInputSessionAt(100, 25);
+        if (UIHelper.isIOS()) {
+            await UIHelper.tapAt(290, 30);
+            await UIHelper.resignFirstResponder();
+            await UIHelper.waitForKeyboardToHide();
+        } else {
+            await UIHelper.typeCharacter("downArrow");
+            await UIHelper.typeCharacter("\r");
+        }
+        resolve();
+    });
+}
 
-UIHelper.activateElement(input);
-eventSender.keyDown("downArrow");
-eventSender.keyDown("\r");
-shouldBe('input.value', '"Orange"');
+(async () => {
+    await focusInputFieldAndChooseFirstSuggestion();
+    first.textContent = fruit.value;
 
-input.value = "a";
+    fruit.value = "a";
 
-UIHelper.activateElement(input);
-eventSender.keyDown("downArrow");
-eventSender.keyDown("\r");
-shouldBe('input.value', '"Apple"');
+    await focusInputFieldAndChooseFirstSuggestion();
+    second.textContent = fruit.value;
 
+    testRunner.notifyDone();
+})();
 </script>
-
-<script src="../../../resources/js-test-post.js"></script>
 </body>
 </html>
index c80a49a..6008363 100644 (file)
@@ -3111,7 +3111,6 @@ fast/block/block-only/absolute-position-min-max-height.html [ Failure ]
 # Datalist
 webkit.org/b/186714 fast/forms/datalist/datalist-show-hide.html [ Skip ]
 webkit.org/b/186714 fast/forms/datalist/datalist-textinput-keydown.html [ Skip ]
-webkit.org/b/190621 fast/forms/datalist/datalist-textinput-suggestions-order.html [ Skip ]
 webkit.org/b/190613 fast/forms/datalist/update-range-with-datalist.html [ Skip ]
 
 # We are only accepting GLSL3 for macOS. 
index ac0d56f..a6944f0 100644 (file)
@@ -132,6 +132,9 @@ window.UIHelper = class UIHelper {
 
     static waitForKeyboardToHide()
     {
+        if (!this.isWebKit2() || !this.isIOS())
+            return Promise.resolve();
+
         return new Promise(resolve => {
             testRunner.runUIScript(`
                 (function() {
@@ -350,4 +353,12 @@ window.UIHelper = class UIHelper {
 
         return new Promise(resolve => testRunner.runUIScript(`uiController.setViewScale(${scale})`, resolve));
     }
+
+    static resignFirstResponder()
+    {
+        if (!this.isWebKit2())
+            return Promise.resolve();
+
+        return new Promise(resolve => testRunner.runUIScript(`uiController.resignFirstResponder()`, resolve));
+    }
 }
index c5fb710..be1fcbf 100644 (file)
@@ -1,3 +1,70 @@
+2018-10-19  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] [Datalist] Can't pick datalist suggestions in a stock WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=190621
+        <rdar://problem/45310649>
+
+        Reviewed by Tim Horton.
+
+        Fixes the bug by refactoring datalist suggestion information on iOS; currently, we override text suggestions on
+        _WKFormInputSession. This only works for a few internal clients (including Safari) that set a _WKInputDelegate
+        and also implement either -_webView:willStartInputSession: or -_webView:didStartInputSession:, which is
+        necessary in order to ensure that WebKit creates and maintains a form input session.
+
+        The two pieces of information that datalist code needs to vend to WKContentView are a list of UITextSuggestions
+        and a custom input view, which are both currently properties of _WKFormInputSession. This patch lifts these out
+        of the input session and makes them properties of WKContentView, which are used in
+        WebDataListSuggestionsDropdownIOS.
+
+        Test: fast/forms/datalist/datalist-textinput-suggestions-order.html
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+
+        Add new properties to WKContentView: an input view for datalist suggestions, and a list of text suggestions.
+
+        (-[WKFormInputSession setSuggestions:]):
+        (-[WKContentView setupInteraction]):
+        (-[WKContentView cleanupInteraction]):
+        (-[WKContentView _endEditing]):
+
+        Pull out common logic when resigning first responder or tabbing to the next or previous text field into a new
+        helper. This helper notifies `_inputPeripheral`, `_formInputSession`, and `_dataListTextSuggestionsInputView`
+        when editing has ended; the input peripheral and suggestions input view use this chance to send the value of the
+        form control to the web process.
+
+        (-[WKContentView resignFirstResponderForWebView]):
+        (-[WKContentView inputView]):
+
+        If a custom input view is not set but we have an input view for a datalist's text suggestions, use the datalist
+        input view.
+
+        (-[WKContentView accessoryTab:]):
+        (-[WKContentView _stopAssistingNode]):
+
+        Clear datalist state on WKContentView.
+
+        (-[WKContentView dataListTextSuggestionsInputView]):
+        (-[WKContentView dataListTextSuggestions]):
+        (-[WKContentView setDataListTextSuggestionsInputView:]):
+        (-[WKContentView setDataListTextSuggestions:]):
+        (-[WKContentView updateTextSuggestionsForInputDelegate]):
+
+        Pull out logic for setting suggestions on UIKit's `inputDelegate` (i.e. UIKeyboardImpl). We now first consult
+        internally-vended text suggestions from _WKFormInputSession; if an internal client has not overridden our text
+        suggestions, then we simply use suggestions from the current datalist (if present).
+
+        * UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm:
+        (-[WKDataListSuggestionsPicker updateWithInformation:]):
+        (-[WKDataListSuggestionsPicker showSuggestionsDropdown:activationType:]):
+        (-[WKDataListSuggestionsPicker invalidate]):
+        (-[WKDataListSuggestionsPopover updateWithInformation:]):
+        (-[WKDataListSuggestionsPopover showSuggestionsDropdown:activationType:]):
+        (-[WKDataListSuggestionsPopover didSelectOptionAtIndex:]):
+
+        Change all the places that currently manipulate WKContentView's form input session to directly set text
+        suggestions and the text suggestion input view on the content view instead.
+
 2018-10-19  John Wilander  <wilander@apple.com>
 
         Only cap lifetime of persistent cookies created client-side through document.cookie when resource load statistics is enabled
index 8e3cdfe..07a4ad3 100644 (file)
@@ -159,6 +159,7 @@ struct WKAutoCorrectionData {
 }
 
 @class WKFocusedElementInfo;
+@protocol WKFormControl;
 
 @interface WKFormInputSession : NSObject <_WKFormInputSession>
 
@@ -247,6 +248,11 @@ struct WKAutoCorrectionData {
 
     RetainPtr<WKKeyboardScrollViewAnimator> _keyboardScrollingAnimator;
 
+#if ENABLE(DATALIST_ELEMENT)
+    RetainPtr<UIView <WKFormControl>> _dataListTextSuggestionsInputView;
+    RetainPtr<NSArray<UITextSuggestion *>> _dataListTextSuggestions;
+#endif
+
     BOOL _isEditable;
     BOOL _showingTextStyleOptions;
     BOOL _hasValidPositionInformation;
@@ -311,6 +317,11 @@ struct WKAutoCorrectionData {
 @property (nonatomic, readonly) UIWebFormAccessory *formAccessoryView;
 @property (nonatomic) BOOL suppressAssistantSelectionView;
 
+#if ENABLE(DATALIST_ELEMENT)
+@property (nonatomic, strong) UIView <WKFormControl> *dataListTextSuggestionsInputView;
+@property (nonatomic, strong) NSArray<UITextSuggestion *> *dataListTextSuggestions;
+#endif
+
 - (void)setupInteraction;
 - (void)cleanupInteraction;
 
@@ -392,6 +403,10 @@ DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW(_pasteAsQuotation)
 
 - (void)reloadContextViewForPresentedListViewController;
 
+#if ENABLE(DATALIST_ELEMENT)
+- (void)updateTextSuggestionsForInputDelegate;
+#endif
+
 @end
 
 @interface WKContentView (WKTesting)
index 46d454c..4565073 100644 (file)
@@ -395,15 +395,11 @@ const CGFloat minimumTapHighlightRadius = 2.0;
 
 - (void)setSuggestions:(NSArray<UITextSuggestion *> *)suggestions
 {
-    // Suggestions that come from a <datalist> should not be overwritten by other clients.
-#if ENABLE(DATALIST_ELEMENT)
-    if ([_contentView assistedNodeInformation].hasSuggestions && ![suggestions.firstObject isKindOfClass:[WKDataListTextSuggestion class]])
+    if (suggestions == _suggestions || [suggestions isEqualToArray:_suggestions.get()])
         return;
-#endif
 
-    id <UITextInputSuggestionDelegate> suggestionDelegate = (id <UITextInputSuggestionDelegate>)[_contentView inputDelegate];
     _suggestions = adoptNS([suggestions copy]);
-    [suggestionDelegate setSuggestions:suggestions];
+    [_contentView updateTextSuggestionsForInputDelegate];
 }
 
 - (BOOL)requiresStrongPasswordAssistance
@@ -712,6 +708,11 @@ static inline bool hasAssistedNode(WebKit::AssistedNodeInformation assistedNodeI
     _needsDeferredEndScrollingSelectionUpdate = NO;
     _isChangingFocus = NO;
     _isBlurringFocusedNode = NO;
+
+#if ENABLE(DATALIST_ELEMENT)
+    _dataListTextSuggestionsInputView = nil;
+    _dataListTextSuggestions = nil;
+#endif
 }
 
 - (void)cleanupInteraction
@@ -807,6 +808,11 @@ static inline bool hasAssistedNode(WebKit::AssistedNodeInformation assistedNodeI
     
     [_keyboardScrollingAnimator invalidate];
     _keyboardScrollingAnimator = nil;
+
+#if ENABLE(DATALIST_ELEMENT)
+    _dataListTextSuggestionsInputView = nil;
+    _dataListTextSuggestions = nil;
+#endif
 }
 
 - (void)_removeDefaultGestureRecognizers
@@ -968,6 +974,15 @@ static inline bool hasAssistedNode(WebKit::AssistedNodeInformation assistedNodeI
     return YES;
 }
 
+- (void)_endEditing
+{
+    [_inputPeripheral endEditing];
+    [_formInputSession endEditing];
+#if ENABLE(DATALIST_ELEMENT)
+    [_dataListTextSuggestionsInputView controlEndEditing];
+#endif
+}
+
 - (BOOL)canBecomeFirstResponder
 {
     return _becomingFirstResponder;
@@ -1017,9 +1032,7 @@ static inline bool hasAssistedNode(WebKit::AssistedNodeInformation assistedNodeI
     _resigningFirstResponder = YES;
     if (!_webView._retainingActiveFocusedState) {
         // We need to complete the editing operation before we blur the element.
-        [_inputPeripheral endEditing];
-        [_formInputSession endEditing];
-
+        [self _endEditing];
         _page->blurAssistedNode();
     }
 
@@ -1362,7 +1375,15 @@ static NSValue *nsSizeForTapHighlightBorderRadius(WebCore::IntSize borderRadius,
     } else
         [self _displayFormNodeInputView];
 
-    return [_formInputSession customInputView] ?: [_inputPeripheral assistantView];
+    if (UIView *customInputView = [_formInputSession customInputView])
+        return customInputView;
+
+#if ENABLE(DATALIST_ELEMENT)
+    if (_dataListTextSuggestionsInputView)
+        return _dataListTextSuggestionsInputView.get();
+#endif
+
+    return [_inputPeripheral assistantView];
 }
 
 - (CGRect)_selectionClipRect
@@ -3170,9 +3191,7 @@ static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoi
 
 - (void)accessoryTab:(BOOL)isNext
 {
-    [_formInputSession endEditing];
-
-    [_inputPeripheral endEditing];
+    [self _endEditing];
     _inputPeripheral = nil;
 
     _didAccessoryTabInitiateFocus = YES; // Will be cleared in either -_displayFormNodeInputView or -cleanupInteraction.
@@ -4322,6 +4341,11 @@ static bool isAssistableInputType(InputType type)
     [_formInputSession invalidate];
     _formInputSession = nil;
 
+#if ENABLE(DATALIST_ELEMENT)
+    _dataListTextSuggestionsInputView = nil;
+    _dataListTextSuggestions = nil;
+#endif
+
     BOOL editableChanged = [self setIsEditable:NO];
 
     _assistedNodeInformation.elementType = InputType::None;
@@ -4740,6 +4764,62 @@ static bool isAssistableInputType(InputType type)
         [_textSelectionAssistant activateSelection];
 }
 
+#if ENABLE(DATALIST_ELEMENT)
+
+- (UIView <WKFormControl> *)dataListTextSuggestionsInputView
+{
+    return _dataListTextSuggestionsInputView.get();
+}
+
+- (NSArray<UITextSuggestion *> *)dataListTextSuggestions
+{
+    return _dataListTextSuggestions.get();
+}
+
+- (void)setDataListTextSuggestionsInputView:(UIView <WKFormControl> *)suggestionsInputView
+{
+    if (_dataListTextSuggestionsInputView == suggestionsInputView)
+        return;
+
+    _dataListTextSuggestionsInputView = suggestionsInputView;
+
+    if (![_formInputSession customInputView])
+        [self reloadInputViews];
+}
+
+- (void)setDataListTextSuggestions:(NSArray<UITextSuggestion *> *)textSuggestions
+{
+    if (textSuggestions == _dataListTextSuggestions || [textSuggestions isEqualToArray:_dataListTextSuggestions.get()])
+        return;
+
+    _dataListTextSuggestions = textSuggestions;
+
+    if (![_formInputSession suggestions].count)
+        [self updateTextSuggestionsForInputDelegate];
+}
+
+#endif
+
+- (void)updateTextSuggestionsForInputDelegate
+{
+    // Text suggestions vended from clients take precedence over text suggestions from a focused form control with a datalist.
+    id <UITextInputSuggestionDelegate> inputDelegate = (id <UITextInputSuggestionDelegate>)self.inputDelegate;
+    NSArray<UITextSuggestion *> *formInputSessionSuggestions = [_formInputSession suggestions];
+    if (formInputSessionSuggestions.count) {
+        [inputDelegate setSuggestions:formInputSessionSuggestions];
+        return;
+    }
+
+#if ENABLE(DATALIST_ELEMENT)
+    if ([_dataListTextSuggestions count]) {
+        [inputDelegate setSuggestions:_dataListTextSuggestions.get()];
+        return;
+    }
+#endif
+
+    [inputDelegate setSuggestions:nil];
+}
+
 - (void)_showPlaybackTargetPicker:(BOOL)hasVideo fromRect:(const IntRect&)elementRect routeSharingPolicy:(WebCore::RouteSharingPolicy)routeSharingPolicy routingContextUID:(NSString *)routingContextUID
 {
 #if ENABLE(AIRPLAY_PICKER)
index ab64cfa..3f58f71 100644 (file)
@@ -32,7 +32,6 @@
 #import "WKContentViewInteraction.h"
 #import "WKFormPeripheral.h"
 #import "WKFormPopover.h"
-#import "_WKFormInputSession.h"
 
 static const CGFloat maxVisibleSuggestions = 5;
 static const CGFloat suggestionsPopoverCellHeight = 44;
@@ -220,12 +219,12 @@ void WebDataListSuggestionsDropdownIOS::didSelectOption(const String& selectedOp
 {
     [super updateWithInformation:WTFMove(information)];
     if (information.activationType != WebCore::DataListSuggestionActivationType::IndicatorClicked) {
-        [[self.view _formInputSession] setCustomInputView:nil];
-        [[self.view _formInputSession] setSuggestions:[self textSuggestions]];
+        self.view.dataListTextSuggestionsInputView = nil;
+        self.view.dataListTextSuggestions = self.textSuggestions;
         return;
     }
 
-    [[self.view _formInputSession] setCustomInputView:_pickerView.get()];
+    self.view.dataListTextSuggestionsInputView = _pickerView.get();
 
     [_pickerView reloadAllComponents];
     [_pickerView selectRow:0 inComponent:0 animated:NO];
@@ -235,10 +234,10 @@ void WebDataListSuggestionsDropdownIOS::didSelectOption(const String& selectedOp
 {
     [super showSuggestionsDropdown:dropdown activationType:activationType];
     if (activationType == WebCore::DataListSuggestionActivationType::IndicatorClicked) {
-        [[self.view _formInputSession] setCustomInputView:_pickerView.get()];
+        self.view.dataListTextSuggestionsInputView = _pickerView.get();
         [_pickerView selectRow:0 inComponent:0 animated:NO];
     } else
-        [[self.view _formInputSession] setSuggestions:[self textSuggestions]];
+        self.view.dataListTextSuggestions = self.textSuggestions;
 }
 
 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
@@ -258,8 +257,8 @@ void WebDataListSuggestionsDropdownIOS::didSelectOption(const String& selectedOp
 
 - (void)invalidate
 {
-    if ([[self.view _formInputSession] customInputView] == _pickerView.get())
-        [[self.view _formInputSession] setCustomInputView:nil];
+    if (self.view.dataListTextSuggestionsInputView == _pickerView.get())
+        self.view.dataListTextSuggestionsInputView = nil;
 
     [_pickerView setDelegate:nil];
     [_pickerView setDataSource:nil];
@@ -307,7 +306,7 @@ void WebDataListSuggestionsDropdownIOS::didSelectOption(const String& selectedOp
 {
     [super updateWithInformation:WTFMove(information)];
     [_suggestionsViewController reloadData];
-    [[self.view _formInputSession] setSuggestions:[self textSuggestions]];
+    self.view.dataListTextSuggestions = self.textSuggestions;
 }
 
 - (void)showSuggestionsDropdown:(WebKit::WebDataListSuggestionsDropdownIOS *)dropdown activationType:(WebCore::DataListSuggestionActivationType)activationType
@@ -317,7 +316,7 @@ void WebDataListSuggestionsDropdownIOS::didSelectOption(const String& selectedOp
     _suggestionsViewController = adoptNS([[WKDataListSuggestionsViewController alloc] initWithStyle:UITableViewStylePlain]);
     [_suggestionsViewController setControl:self];
     [_suggestionsViewController reloadData];
-    [[self.view _formInputSession] setSuggestions:[self textSuggestions]];
+    self.view.dataListTextSuggestions = self.textSuggestions;
 
 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
     [_popover setPopoverController:[[[UIPopoverController alloc] initWithContentViewController:_suggestionsViewController.get()] autorelease]];
@@ -335,7 +334,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END
 {
     [super didSelectOptionAtIndex:index];
     [[_popover popoverController] dismissPopoverAnimated:YES];
-    [[self.view _formInputSession] setSuggestions:@[ [WKDataListTextSuggestion textSuggestionWithInputText:[self suggestionAtIndex:index]] ]];
+    self.view.dataListTextSuggestions = @[ [WKDataListTextSuggestion textSuggestionWithInputText:[self suggestionAtIndex:index]] ];
 }
 
 @end
index 15c0c31..91d9af0 100644 (file)
@@ -1,3 +1,34 @@
+2018-10-19  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] [Datalist] Can't pick datalist suggestions in a stock WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=190621
+        <rdar://problem/45310649>
+
+        Reviewed by Tim Horton.
+
+        Add a UIScriptController hook to resign first responder on WKWebView. See LayoutTests/ChangeLog for more detail.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::resignFirstResponder):
+        * DumpRenderTree/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::resignFirstResponder):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::resignFirstResponder):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/UIScriptControllerCocoa.mm:
+        (WTR::UIScriptController::resignFirstResponder):
+        * WebKitTestRunner/ios/PlatformWebViewIOS.mm:
+        (WTR::PlatformWebView::makeWebViewFirstResponder):
+
+        Implement this method stub on iOS, to ensure that TestController::resetStateToConsistentValues restores first
+        responder on the WKWebView when running iOS layout tests.
+
+        * WebKitTestRunner/ios/TestControllerIOS.mm:
+        (WTR::TestController::platformResetStateToConsistentValues):
+
+        After resigning first responder to dismiss any on-screen keyboard, ensure that we restore first responder.
+
 2018-10-19  Dean Jackson  <dino@apple.com>
 
         ASSERTION FAILED: !frame().animation().hasAnimations() in WebCore::FrameView::didDestroyRenderTree()
index cee98cb..6abf845 100644 (file)
@@ -82,6 +82,10 @@ void UIScriptController::zoomToScale(double scale, JSValueRef callback)
     });
 }
 
+void UIScriptController::resignFirstResponder()
+{
+}
+
 void UIScriptController::setViewScale(double)
 {
 }
index f4a3343..49cc4b7 100644 (file)
@@ -89,6 +89,10 @@ void UIScriptController::zoomToScale(double scale, JSValueRef callback)
     });
 }
 
+void UIScriptController::resignFirstResponder()
+{
+}
+
 void UIScriptController::setViewScale(double)
 {
 }
index bc50082..4ace4ed 100644 (file)
@@ -219,6 +219,8 @@ interface UIScriptController {
 
     void setViewScale(double scale);
 
+    void resignFirstResponder();
+
     void scrollToOffset(long x, long y); // Initiate an animated scroll in the UI process.
     attribute object didEndScrollingCallback;
 
index 807745f..8e1fb9f 100644 (file)
@@ -214,6 +214,10 @@ void UIScriptController::setViewScale(double)
 {
 }
 
+void UIScriptController::resignFirstResponder()
+{
+}
+
 void UIScriptController::simulateAccessibilitySettingsChangeNotification(JSValueRef)
 {
 }
index 60d5966..8c6b784 100644 (file)
@@ -68,6 +68,8 @@ public:
     void zoomToScale(double scale, JSValueRef callback);
     void setViewScale(double);
 
+    void resignFirstResponder();
+
     void simulateAccessibilitySettingsChangeNotification(JSValueRef callback);
 
     void touchDownAtPoint(long x, long y, long touchCount, JSValueRef callback);
index 208c748..7f5b6c5 100644 (file)
@@ -42,4 +42,11 @@ void UIScriptController::setViewScale(double scale)
 #endif
 }
 
+void UIScriptController::resignFirstResponder()
+{
+#if WK_API_ENABLED
+    [TestController::singleton().mainWebView()->platformView() resignFirstResponder];
+#endif
+}
+
 } // namespace WTR
index 9cf3d42..4809196 100644 (file)
@@ -286,8 +286,7 @@ void PlatformWebView::removeChromeInputField()
 
 void PlatformWebView::makeWebViewFirstResponder()
 {
-    // FIXME: iOS equivalent?
-    // [m_window makeFirstResponder:m_view];
+    [m_view becomeFirstResponder];
 }
 
 void PlatformWebView::changeWindowScaleIfNeeded(float)
index b9dcfd8..ab4af11 100644 (file)
@@ -107,6 +107,7 @@ void TestController::platformResetStateToConsistentValues()
 
     [[UIDevice currentDevice] setOrientation:UIDeviceOrientationPortrait animated:NO];
     
+    BOOL shouldRestoreFirstResponder = NO;
     if (PlatformWebView* platformWebView = mainWebView()) {
         TestRunnerWKWebView *webView = platformWebView->platformView();
         webView._stableStateOverride = nil;
@@ -121,10 +122,13 @@ void TestController::platformResetStateToConsistentValues()
         [scrollView setContentOffset:CGPointZero];
 
         if (webView.interactingWithFormControl)
-            [webView resignFirstResponder];
+            shouldRestoreFirstResponder = [webView resignFirstResponder];
     }
 
     runUntil(isDoneWaitingForKeyboardToDismiss, m_currentInvocation->shortTimeout());
+
+    if (shouldRestoreFirstResponder)
+        [mainWebView()->platformView() becomeFirstResponder];
 }
 
 void TestController::platformConfigureViewForTest(const TestInvocation& test)