Title: [272659] trunk
Revision
272659
Author
wenson_hs...@apple.com
Date
2021-02-10 09:07:58 -0800 (Wed, 10 Feb 2021)

Log Message

[watchOS] Adopt PUICQuickboardController for text input
https://bugs.webkit.org/show_bug.cgi?id=221649

Reviewed by Tim Horton.

Source/WebKit:

Refactor text input on watchOS to use `PUICQuickboardController`, instead of custom WebKit subclasses of
`PUICQuickboardListViewController`. This new API serves the same purpose as the current list view controller
subclass, by providing a view that offers options to dictate, scribble, and choose text suggestions. See below
for more details.

For the time being, this new `PUICQuickboardController` doesn't provide a way to render a custom header view
for us to show the domain name; to avoid introducing a security or privacy bug in the interim, guard this behind
a WebKit-exposed runtime-enabled setting that's off by default. We can remove this setting and instead just use
the `HAVE(QUICKBOARD_CONTROLLER)` build-time flag once support for the custom header view is complete.

* Platform/spi/watchos/PepperUICoreSPI.h:

Include a couple of new headers if `HAVE(QUICKBOARD_CONTROLLER)` is set.

* UIProcess/ios/WKContentViewInteraction.h:

Add a `_presentedQuickboardController` instance variable if `HAVE(QUICKBOARD_CONTROLLER)` is set.

* UIProcess/ios/WKContentViewInteraction.mm:

Add protocol conformance to `PUICQuickboardControllerDelegate`.

(-[WKContentView _updateInteractionTintColor:]):
(-[WKContentView tintColorDidChange]):

Refactor this to take `UITextInputTraits`, instead of using `_traits`.

(-[WKContentView textInputTraits]):
(-[WKContentView _updateTextInputTraits:]):

Refactor this to modify the given set of text input traits given current focused element information state,
rather than operating on the `_traits` instance variable. This refactoring allows us to share logic when setting
up text input traits on both iOS and watchOS; in the former case, we're setting up a set of cached `_traits` on
the content view, but in the latter case, we're modifying a newly created `PUICTextInputContext` that we'll then
set on the `PUICQuickboardController`.

(-[WKContentView _createQuickboardController:]):

Add a helper method to create and return a new `PUICQuickboardController` that's suitable for the current
focused element's state.

(-[WKContentView presentViewControllerForCurrentFocusedElement]):

Present the view controller for the focused element, by either using a custom WebKit-owned
`PUICQuickboardListViewController` subclass, or by using a new `PUICQuickboardController` with a custom text
input context. Note that in the latter case, we may not be able to invoke the UI delegate hook
`-_webView:didPresentFocusedElementViewController:`, since the presenting view controller's transition
coordinator may not have been created yet in the scenario where we're spinning up the Quickboard view service
for the first time, since the call to establish the XPC connection is asynchronous in PepperUICore, and we don't
attempt to present the remote view controller until the connection is established. This makes it so that we
can't rely on the `-didPresentFocusedElementViewController:` hook in WebKitTestRunner to know when a
`PUICQuickboardController` has finished presenting.

To work around this, we teach the test harness to override `-presentViewController:animated:completion:` and
call into the test runner's `WKWebView` subclass when a remote quickboard controller is done presenting. In the
longer term, we will require SPI from PepperUICore to present a `PUICQuickboardController` with a completion
block.

(-[WKContentView _isPresentingFullScreenInputView]):

A "full screen input view" for the focused element is now present if either `_presentedQuickboardController` is
set, or `_presentedFullScreenInputViewController` is set.

(-[WKContentView dismissAllInputViewControllers:]):

Dismiss either the currently presented `_presentedQuickboardController` or
`_presentedFullScreenInputViewController`, and invoke the UI delegate method
`-_webView:didDismissFocusedElementViewController:` when finished.

(-[WKContentView quickboardController:textInputValueDidChange:]):
(-[WKContentView quickboardControllerTextInputValueCancelled:]):
(-[WKContentView dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:]):
(-[WKContentView textContentTypeForQuickboard]):

Refactor `-_updateTextInputTraits:` to call a helper method (`-textContentTypeForQuickboard`) on watchOS to
convert the current focused element information to a `UITextContentType`.

(-[WKContentView textContentTypeForListViewController:]):
(-[WKContentView _simulateTextEntered:]):
(-[WKContentView textContentTypeForTesting]):
(-[WKContentView formInputLabel]):

Update a few internal testing hooks to handle the case where we have a `_presentedQuickboardController` instead
of a `_presentedFullScreenInputViewController`.

(-[WKContentView _updateInteractionTintColor]): Deleted.
* UIProcess/ios/forms/WKTextInputListViewController.mm:
(-[WKTextInputListViewController additionalTrayButtons]): Suppress a deprecation warning.

Source/WTF:

Add a new internal setting. See other ChangeLogs for more detail.

* Scripts/Preferences/WebPreferencesInternal.yaml:

Tools:

Make some small tweaks to WebKitTestRunner in support of using `PUICQuickboardController` for text input.

* WebKitTestRunner/Configurations/Base.xcconfig:
* WebKitTestRunner/Configurations/WebKitTestRunnerApp.xcconfig:

Adjust the test harness on watchOS to link against PepperUICore, so that we can reference the
`PUICQuickboardViewController` and `PUICQuickboardRemoteViewController` classes below.

* WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
* WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
(isQuickboardViewController):
(-[TestRunnerWKWebView _didPresentViewController:]):

Override `-presentViewController:animated:completion:` on the view controller below, and call this new method
on the `TestRunnerWKWebView` when a view controller is finished presenting. We need to do this to ensure that
the input view presentation callbacks continue to work after adopting `PUICQuickboardController`; see WebKit
ChangeLog for more details.

* WebKitTestRunner/ios/PlatformWebViewIOS.mm:
(-[PlatformWebViewController presentViewController:animated:completion:]):

LayoutTests:

Enable `QuickboardControllerForTextInputEnabled`, on relevant versions of watchOS.

* fast/forms/watchos/delete-content-in-text-field.html:
* fast/forms/watchos/edit-text-field-calls-injected-bundle.html:
* fast/forms/watchos/form-control-label-text.html:
* fast/forms/watchos/time-picker-value-change.html:
* fast/forms/watchos/username-text-content-type.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (272658 => 272659)


--- trunk/LayoutTests/ChangeLog	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/LayoutTests/ChangeLog	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,3 +1,18 @@
+2021-02-10  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [watchOS] Adopt PUICQuickboardController for text input
+        https://bugs.webkit.org/show_bug.cgi?id=221649
+
+        Reviewed by Tim Horton.
+
+        Enable `QuickboardControllerForTextInputEnabled`, on relevant versions of watchOS.
+
+        * fast/forms/watchos/delete-content-in-text-field.html:
+        * fast/forms/watchos/edit-text-field-calls-injected-bundle.html:
+        * fast/forms/watchos/form-control-label-text.html:
+        * fast/forms/watchos/time-picker-value-change.html:
+        * fast/forms/watchos/username-text-content-type.html:
+
 2021-02-10  Chris Gambrell  <cgambr...@apple.com>
 
         [LayoutTests] Convert http/tests/css convert PHP to Python

Modified: trunk/LayoutTests/fast/forms/watchos/delete-content-in-text-field.html (272658 => 272659)


--- trunk/LayoutTests/fast/forms/watchos/delete-content-in-text-field.html	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/LayoutTests/fast/forms/watchos/delete-content-in-text-field.html	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,4 +1,4 @@
-<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true QuickboardControllerForTextInputEnabled=true ] -->
 <html>
 <meta name="viewport" content="width=device-width">
 <head>

Modified: trunk/LayoutTests/fast/forms/watchos/edit-text-field-calls-injected-bundle.html (272658 => 272659)


--- trunk/LayoutTests/fast/forms/watchos/edit-text-field-calls-injected-bundle.html	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/LayoutTests/fast/forms/watchos/edit-text-field-calls-injected-bundle.html	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,4 +1,4 @@
-<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true QuickboardControllerForTextInputEnabled=true ] -->
 <html>
 <meta name="viewport" content="width=device-width">
 <head>

Modified: trunk/LayoutTests/fast/forms/watchos/form-control-label-text.html (272658 => 272659)


--- trunk/LayoutTests/fast/forms/watchos/form-control-label-text.html	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/LayoutTests/fast/forms/watchos/form-control-label-text.html	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,4 +1,4 @@
-<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true QuickboardControllerForTextInputEnabled=true ] -->
 <html>
 <meta name="viewport" content="width=device-width">
 <head>

Modified: trunk/LayoutTests/fast/forms/watchos/time-picker-value-change.html (272658 => 272659)


--- trunk/LayoutTests/fast/forms/watchos/time-picker-value-change.html	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/LayoutTests/fast/forms/watchos/time-picker-value-change.html	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,4 +1,4 @@
-<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true QuickboardControllerForTextInputEnabled=true ] -->
 <html>
 <meta name="viewport" content="width=device-width">
 <head>

Modified: trunk/LayoutTests/fast/forms/watchos/username-text-content-type.html (272658 => 272659)


--- trunk/LayoutTests/fast/forms/watchos/username-text-content-type.html	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/LayoutTests/fast/forms/watchos/username-text-content-type.html	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,4 +1,4 @@
-<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true QuickboardControllerForTextInputEnabled=true ] -->
 <html>
 <meta name="viewport" content="width=device-width">
 <head>

Modified: trunk/Source/WTF/ChangeLog (272658 => 272659)


--- trunk/Source/WTF/ChangeLog	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WTF/ChangeLog	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,3 +1,14 @@
+2021-02-10  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [watchOS] Adopt PUICQuickboardController for text input
+        https://bugs.webkit.org/show_bug.cgi?id=221649
+
+        Reviewed by Tim Horton.
+
+        Add a new internal setting. See other ChangeLogs for more detail.
+
+        * Scripts/Preferences/WebPreferencesInternal.yaml:
+
 2021-02-09  Devin Rousso  <drou...@apple.com>
 
         [iOS] Add support for the language/subtitle tracks button using `UIMenu`

Modified: trunk/Source/WTF/Scripts/Preferences/WebPreferencesInternal.yaml (272658 => 272659)


--- trunk/Source/WTF/Scripts/Preferences/WebPreferencesInternal.yaml	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WTF/Scripts/Preferences/WebPreferencesInternal.yaml	2021-02-10 17:07:58 UTC (rev 272659)
@@ -599,6 +599,17 @@
     WebCore:
       default: false
 
+QuickboardControllerForTextInputEnabled:
+  type: bool
+  humanReadableName: "Quickboard controller"
+  humanReadableDescription: "Enable quickboard controller for text input"
+  condition: HAVE(QUICKBOARD_CONTROLLER)
+  webcoreBinding: none
+  exposed: [ WebKit ]
+  defaultValue:
+    WebKit:
+      default: false
+
 ResourceLoadSchedulingEnabled:
   type: bool
   humanReadableName: "Resource Load Scheduling"

Modified: trunk/Source/WebKit/ChangeLog (272658 => 272659)


--- trunk/Source/WebKit/ChangeLog	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WebKit/ChangeLog	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,3 +1,99 @@
+2021-02-10  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [watchOS] Adopt PUICQuickboardController for text input
+        https://bugs.webkit.org/show_bug.cgi?id=221649
+
+        Reviewed by Tim Horton.
+
+        Refactor text input on watchOS to use `PUICQuickboardController`, instead of custom WebKit subclasses of
+        `PUICQuickboardListViewController`. This new API serves the same purpose as the current list view controller
+        subclass, by providing a view that offers options to dictate, scribble, and choose text suggestions. See below
+        for more details.
+
+        For the time being, this new `PUICQuickboardController` doesn't provide a way to render a custom header view
+        for us to show the domain name; to avoid introducing a security or privacy bug in the interim, guard this behind
+        a WebKit-exposed runtime-enabled setting that's off by default. We can remove this setting and instead just use
+        the `HAVE(QUICKBOARD_CONTROLLER)` build-time flag once support for the custom header view is complete.
+
+        * Platform/spi/watchos/PepperUICoreSPI.h:
+
+        Include a couple of new headers if `HAVE(QUICKBOARD_CONTROLLER)` is set.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+
+        Add a `_presentedQuickboardController` instance variable if `HAVE(QUICKBOARD_CONTROLLER)` is set.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+
+        Add protocol conformance to `PUICQuickboardControllerDelegate`.
+
+        (-[WKContentView _updateInteractionTintColor:]):
+        (-[WKContentView tintColorDidChange]):
+
+        Refactor this to take `UITextInputTraits`, instead of using `_traits`.
+
+        (-[WKContentView textInputTraits]):
+        (-[WKContentView _updateTextInputTraits:]):
+
+        Refactor this to modify the given set of text input traits given current focused element information state,
+        rather than operating on the `_traits` instance variable. This refactoring allows us to share logic when setting
+        up text input traits on both iOS and watchOS; in the former case, we're setting up a set of cached `_traits` on
+        the content view, but in the latter case, we're modifying a newly created `PUICTextInputContext` that we'll then
+        set on the `PUICQuickboardController`.
+
+        (-[WKContentView _createQuickboardController:]):
+
+        Add a helper method to create and return a new `PUICQuickboardController` that's suitable for the current
+        focused element's state.
+
+        (-[WKContentView presentViewControllerForCurrentFocusedElement]):
+
+        Present the view controller for the focused element, by either using a custom WebKit-owned
+        `PUICQuickboardListViewController` subclass, or by using a new `PUICQuickboardController` with a custom text
+        input context. Note that in the latter case, we may not be able to invoke the UI delegate hook
+        `-_webView:didPresentFocusedElementViewController:`, since the presenting view controller's transition
+        coordinator may not have been created yet in the scenario where we're spinning up the Quickboard view service
+        for the first time, since the call to establish the XPC connection is asynchronous in PepperUICore, and we don't
+        attempt to present the remote view controller until the connection is established. This makes it so that we
+        can't rely on the `-didPresentFocusedElementViewController:` hook in WebKitTestRunner to know when a
+        `PUICQuickboardController` has finished presenting.
+
+        To work around this, we teach the test harness to override `-presentViewController:animated:completion:` and
+        call into the test runner's `WKWebView` subclass when a remote quickboard controller is done presenting. In the
+        longer term, we will require SPI from PepperUICore to present a `PUICQuickboardController` with a completion
+        block.
+
+        (-[WKContentView _isPresentingFullScreenInputView]):
+
+        A "full screen input view" for the focused element is now present if either `_presentedQuickboardController` is
+        set, or `_presentedFullScreenInputViewController` is set.
+
+        (-[WKContentView dismissAllInputViewControllers:]):
+
+        Dismiss either the currently presented `_presentedQuickboardController` or
+        `_presentedFullScreenInputViewController`, and invoke the UI delegate method
+        `-_webView:didDismissFocusedElementViewController:` when finished.
+
+        (-[WKContentView quickboardController:textInputValueDidChange:]):
+        (-[WKContentView quickboardControllerTextInputValueCancelled:]):
+        (-[WKContentView dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:]):
+        (-[WKContentView textContentTypeForQuickboard]):
+
+        Refactor `-_updateTextInputTraits:` to call a helper method (`-textContentTypeForQuickboard`) on watchOS to
+        convert the current focused element information to a `UITextContentType`.
+
+        (-[WKContentView textContentTypeForListViewController:]):
+        (-[WKContentView _simulateTextEntered:]):
+        (-[WKContentView textContentTypeForTesting]):
+        (-[WKContentView formInputLabel]):
+
+        Update a few internal testing hooks to handle the case where we have a `_presentedQuickboardController` instead
+        of a `_presentedFullScreenInputViewController`.
+
+        (-[WKContentView _updateInteractionTintColor]): Deleted.
+        * UIProcess/ios/forms/WKTextInputListViewController.mm:
+        (-[WKTextInputListViewController additionalTrayButtons]): Suppress a deprecation warning.
+
 2021-02-10  Myles C. Maxfield  <mmaxfi...@apple.com>
 
         Replace ChangeLog entry I accidentally deleted in r272657

Modified: trunk/Source/WebKit/Platform/spi/watchos/PepperUICoreSPI.h (272658 => 272659)


--- trunk/Source/WebKit/Platform/spi/watchos/PepperUICoreSPI.h	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WebKit/Platform/spi/watchos/PepperUICoreSPI.h	2021-02-10 17:07:58 UTC (rev 272659)
@@ -55,6 +55,11 @@
 #import <PepperUICore/PUICQuickboardListCollectionViewItemCell.h>
 #endif
 
+#if HAVE(QUICKBOARD_CONTROLLER)
+#import <PepperUICore/PUICQuickboardController.h>
+#import <PepperUICore/PUICQuickboardRemoteViewController.h>
+#endif
+
 #else // USE(APPLE_INTERNAL_SDK)
 
 NS_ASSUME_NONNULL_BEGIN

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (272658 => 272659)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-02-10 17:07:58 UTC (rev 272659)
@@ -121,6 +121,13 @@
 @class _UILookupGestureRecognizer;
 @class _UIHighlightView;
 
+#if PLATFORM(WATCHOS)
+@class PUICQuickboardViewController;
+#if HAVE(QUICKBOARD_CONTROLLER)
+@class PUICQuickboardController;
+#endif
+#endif
+
 #if USE(APPLE_INTERNAL_SDK)
 #import <WebKitAdditions/WKContentViewInteractionAdditionsBefore.h>
 #endif
@@ -452,12 +459,15 @@
 
 #if PLATFORM(WATCHOS)
     RetainPtr<WKFocusedFormControlView> _focusedFormControlView;
-    RetainPtr<UIViewController> _presentedFullScreenInputViewController;
+#if HAVE(QUICKBOARD_CONTROLLER)
+    RetainPtr<PUICQuickboardController> _presentedQuickboardController;
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+    RetainPtr<PUICQuickboardViewController> _presentedFullScreenInputViewController;
     RetainPtr<UINavigationController> _inputNavigationViewControllerForFullScreenInputs;
 
     BOOL _shouldRestoreFirstResponderStatusAfterLosingFocus;
     BlockPtr<void()> _activeFocusedStateRetainBlock;
-#endif
+#endif // PLATFORM(WATCHOS)
 
 #if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
     std::unique_ptr<WebKit::TextCheckingController> _textCheckingController;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (272658 => 272659)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-02-10 17:07:58 UTC (rev 272659)
@@ -167,6 +167,10 @@
 #import "WKWebEvent.h"
 #endif
 
+#if PLATFORM(WATCHOS)
+#import "PepperUICoreSPI.h"
+#endif
+
 #import <pal/ios/ManagedConfigurationSoftLink.h>
 
 #if HAVE(LINK_PREVIEW) && USE(UICONTEXTMENU)
@@ -187,9 +191,14 @@
 #endif
 
 #if PLATFORM(WATCHOS)
+#if HAVE(QUICKBOARD_CONTROLLER)
+@interface WKContentView (QuickboardControllerSupport) <PUICQuickboardControllerDelegate>
+@end
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+
 @interface WKContentView (WatchSupport) <WKFocusedFormControlViewDelegate, WKSelectMenuListViewControllerDelegate, WKTextInputListViewControllerDelegate>
 @end
-#endif
+#endif // PLATFORM(WATCHOS)
 
 static void *WKContentViewKVOTransformContext = &WKContentViewKVOTransformContext;
 
@@ -3445,9 +3454,9 @@
     return [self _inheritedInteractionTintColor];
 }
 
-- (void)_updateInteractionTintColor
+- (void)_updateInteractionTintColor:(UITextInputTraits *)traits
 {
-    [_traits _setColorsToMatchTintColor:[self _cascadeInteractionTintColor]];
+    [traits _setColorsToMatchTintColor:[self _cascadeInteractionTintColor]];
 }
 
 - (void)tintColorDidChange
@@ -3457,7 +3466,7 @@
     BOOL shouldUpdateTextSelection = self.isFirstResponder && [self canShowNonEmptySelectionView];
     if (shouldUpdateTextSelection)
         [_textInteractionAssistant deactivateSelection];
-    [self _updateInteractionTintColor];
+    [self _updateInteractionTintColor:_traits.get()];
     if (shouldUpdateTextSelection)
         [_textInteractionAssistant activateSelection];
 }
@@ -5164,45 +5173,49 @@
         return _traits.get();
 #endif
 
-    [_traits setSecureTextEntry:_focusedElementInformation.elementType == WebKit::InputType::Password || [_formInputSession forceSecureTextEntry]];
-    [_traits setShortcutConversionType:_focusedElementInformation.elementType == WebKit::InputType::Password ? UITextShortcutConversionTypeNo : UITextShortcutConversionTypeDefault];
+    [self _updateTextInputTraits:_traits.get()];
+    return _traits.get();
+}
 
+- (void)_updateTextInputTraits:(id <UITextInputTraits>)traits
+{
+    traits.secureTextEntry = _focusedElementInformation.elementType == WebKit::InputType::Password || [_formInputSession forceSecureTextEntry];
+
     switch (_focusedElementInformation.enterKeyHint) {
     case WebCore::EnterKeyHint::Enter:
-        [_traits setReturnKeyType:UIReturnKeyDefault];
+        traits.returnKeyType = UIReturnKeyDefault;
         break;
     case WebCore::EnterKeyHint::Done:
-        [_traits setReturnKeyType:UIReturnKeyDone];
+        traits.returnKeyType = UIReturnKeyDone;
         break;
     case WebCore::EnterKeyHint::Go:
-        [_traits setReturnKeyType:UIReturnKeyGo];
+        traits.returnKeyType = UIReturnKeyGo;
         break;
     case WebCore::EnterKeyHint::Next:
-        [_traits setReturnKeyType:UIReturnKeyNext];
+        traits.returnKeyType = UIReturnKeyNext;
         break;
     case WebCore::EnterKeyHint::Search:
-        [_traits setReturnKeyType:UIReturnKeySearch];
+        traits.returnKeyType = UIReturnKeySearch;
         break;
     case WebCore::EnterKeyHint::Send:
-        [_traits setReturnKeyType:UIReturnKeySend];
+        traits.returnKeyType = UIReturnKeySend;
         break;
     default: {
         if (!_focusedElementInformation.formAction.isEmpty())
-            [_traits setReturnKeyType:_focusedElementInformation.elementType == WebKit::InputType::Search ? UIReturnKeySearch : UIReturnKeyGo];
+            traits.returnKeyType = _focusedElementInformation.elementType == WebKit::InputType::Search ? UIReturnKeySearch : UIReturnKeyGo;
     }
     }
 
-    if (_focusedElementInformation.elementType == WebKit::InputType::Password || _focusedElementInformation.elementType == WebKit::InputType::Email || _focusedElementInformation.elementType == WebKit::InputType::URL || _focusedElementInformation.formAction.contains("login")) {
-        [_traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
-        [_traits setAutocorrectionType:UITextAutocorrectionTypeNo];
-    } else {
-        [_traits setAutocapitalizationType:toUITextAutocapitalize(_focusedElementInformation.autocapitalizeType)];
-        [_traits setAutocorrectionType:_focusedElementInformation.isAutocorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo];
-    }
+    BOOL disableAutocorrectAndAutocapitalize = _focusedElementInformation.elementType == WebKit::InputType::Password || _focusedElementInformation.elementType == WebKit::InputType::Email
+        || _focusedElementInformation.elementType == WebKit::InputType::URL || _focusedElementInformation.formAction.contains("login");
+    if ([traits respondsToSelector:@selector(setAutocapitalizationType:)])
+        traits.autocapitalizationType = disableAutocorrectAndAutocapitalize ? UITextAutocapitalizationTypeNone : toUITextAutocapitalize(_focusedElementInformation.autocapitalizeType);
+    if ([traits respondsToSelector:@selector(setAutocorrectionType:)])
+        traits.autocorrectionType = disableAutocorrectAndAutocapitalize ? UITextAutocorrectionTypeNo : (_focusedElementInformation.isAutocorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo);
 
     if (!_focusedElementInformation.isSpellCheckingEnabled) {
-        [_traits setSmartQuotesType:UITextSmartQuotesTypeNo];
-        [_traits setSmartDashesType:UITextSmartDashesTypeNo];
+        traits.smartQuotesType = UITextSmartQuotesTypeNo;
+        traits.smartDashesType = UITextSmartDashesTypeNo;
     }
 
     switch (_focusedElementInformation.inputMode) {
@@ -5210,19 +5223,19 @@
     case WebCore::InputMode::Unspecified:
         switch (_focusedElementInformation.elementType) {
         case WebKit::InputType::Phone:
-            [_traits setKeyboardType:UIKeyboardTypePhonePad];
+            traits.keyboardType = UIKeyboardTypePhonePad;
             break;
         case WebKit::InputType::URL:
-            [_traits setKeyboardType:UIKeyboardTypeURL];
+            traits.keyboardType = UIKeyboardTypeURL;
             break;
         case WebKit::InputType::Email:
-            [_traits setKeyboardType:UIKeyboardTypeEmailAddress];
+            traits.keyboardType = UIKeyboardTypeEmailAddress;
             break;
         case WebKit::InputType::Number:
-            [_traits setKeyboardType:UIKeyboardTypeNumbersAndPunctuation];
+            traits.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
             break;
         case WebKit::InputType::NumberPad:
-            [_traits setKeyboardType:UIKeyboardTypeNumberPad];
+            traits.keyboardType = UIKeyboardTypeNumberPad;
             break;
         case WebKit::InputType::None:
         case WebKit::InputType::ContentEditable:
@@ -5240,66 +5253,75 @@
 #if ENABLE(INPUT_TYPE_COLOR)
         case WebKit::InputType::Color:
 #endif
-            [_traits setKeyboardType:UIKeyboardTypeDefault];
+            traits.keyboardType = UIKeyboardTypeDefault;
         }
         break;
     case WebCore::InputMode::Text:
-        [_traits setKeyboardType:UIKeyboardTypeDefault];
+        traits.keyboardType = UIKeyboardTypeDefault;
         break;
     case WebCore::InputMode::Telephone:
-        [_traits setKeyboardType:UIKeyboardTypePhonePad];
+        traits.keyboardType = UIKeyboardTypePhonePad;
         break;
     case WebCore::InputMode::Url:
-        [_traits setKeyboardType:UIKeyboardTypeURL];
+        traits.keyboardType = UIKeyboardTypeURL;
         break;
     case WebCore::InputMode::Email:
-        [_traits setKeyboardType:UIKeyboardTypeEmailAddress];
+        traits.keyboardType = UIKeyboardTypeEmailAddress;
         break;
     case WebCore::InputMode::Numeric:
-        [_traits setKeyboardType:UIKeyboardTypeNumberPad];
+        traits.keyboardType = UIKeyboardTypeNumberPad;
         break;
     case WebCore::InputMode::Decimal:
-        [_traits setKeyboardType:UIKeyboardTypeDecimalPad];
+        traits.keyboardType = UIKeyboardTypeDecimalPad;
         break;
     case WebCore::InputMode::Search:
-        [_traits setKeyboardType:UIKeyboardTypeWebSearch];
+        traits.keyboardType = UIKeyboardTypeWebSearch;
         break;
     }
 
-    [_traits setTextContentType:contentTypeFromFieldName(_focusedElementInformation.autofillFieldName)];
+#if PLATFORM(WATCHOS)
+    traits.textContentType = self.textContentTypeForQuickboard;
+#else
+    traits.textContentType = contentTypeFromFieldName(_focusedElementInformation.autofillFieldName);
+#endif
 
-    switch (_focusedElementInformation.elementType) {
-    case WebKit::InputType::ContentEditable:
-    case WebKit::InputType::TextArea:
-        [_traits setIsSingleLineDocument:NO];
-        break;
+    auto privateTraits = (id <UITextInputTraits_Private>)traits;
+    if ([privateTraits respondsToSelector:@selector(setIsSingleLineDocument:)]) {
+        switch (_focusedElementInformation.elementType) {
+        case WebKit::InputType::ContentEditable:
+        case WebKit::InputType::TextArea:
+            privateTraits.isSingleLineDocument = NO;
+            break;
 #if ENABLE(INPUT_TYPE_COLOR)
-    case WebKit::InputType::Color:
+        case WebKit::InputType::Color:
 #endif
-    case WebKit::InputType::Date:
-    case WebKit::InputType::DateTimeLocal:
-    case WebKit::InputType::Drawing:
-    case WebKit::InputType::Email:
-    case WebKit::InputType::Month:
-    case WebKit::InputType::Number:
-    case WebKit::InputType::NumberPad:
-    case WebKit::InputType::Password:
-    case WebKit::InputType::Phone:
-    case WebKit::InputType::Search:
-    case WebKit::InputType::Select:
-    case WebKit::InputType::Text:
-    case WebKit::InputType::Time:
-    case WebKit::InputType::URL:
-    case WebKit::InputType::Week:
-        [_traits setIsSingleLineDocument:YES];
-        break;
-    case WebKit::InputType::None:
-        break;
+        case WebKit::InputType::Date:
+        case WebKit::InputType::DateTimeLocal:
+        case WebKit::InputType::Drawing:
+        case WebKit::InputType::Email:
+        case WebKit::InputType::Month:
+        case WebKit::InputType::Number:
+        case WebKit::InputType::NumberPad:
+        case WebKit::InputType::Password:
+        case WebKit::InputType::Phone:
+        case WebKit::InputType::Search:
+        case WebKit::InputType::Select:
+        case WebKit::InputType::Text:
+        case WebKit::InputType::Time:
+        case WebKit::InputType::URL:
+        case WebKit::InputType::Week:
+            privateTraits.isSingleLineDocument = YES;
+            break;
+        case WebKit::InputType::None:
+            break;
+        }
     }
 
-    [self _updateInteractionTintColor];
+    if ([privateTraits respondsToSelector:@selector(setShortcutConversionType:)])
+        privateTraits.shortcutConversionType = _focusedElementInformation.elementType == WebKit::InputType::Password ? UITextShortcutConversionTypeNo : UITextShortcutConversionTypeDefault;
 
-    return _traits.get();
+    if ([traits isKindOfClass:UITextInputTraits.class])
+        [self _updateInteractionTintColor:(UITextInputTraits *)traits];
 }
 
 - (UITextInteractionAssistant *)interactionAssistant
@@ -6445,6 +6467,31 @@
     [self setInputDelegate:nil];
 }
 
+#if HAVE(QUICKBOARD_CONTROLLER)
+
+- (RetainPtr<PUICQuickboardController>)_createQuickboardController:(UIViewController *)presentingViewController
+{
+    auto quickboardController = adoptNS([[PUICQuickboardController alloc] init]);
+
+    auto suggestions = adoptNS([[NSMutableArray<NSString *> alloc] initWithCapacity:[_formInputSession suggestions].count]);
+    for (UITextSuggestion *suggestion in [_formInputSession suggestions])
+        [suggestions addObject:suggestion.inputText];
+
+    auto context = adoptNS([[PUICTextInputContext alloc] init]);
+    [self _updateTextInputTraits:context.get()];
+    [context setSuggestions:suggestions.get()];
+    [context setInitialText:_focusedElementInformation.value];
+    [context setAcceptsEmoji:YES];
+    [quickboardController setQuickboardPresentingViewController:presentingViewController];
+    [quickboardController setExcludedFromScreenCapture:[context isSecureTextEntry]];
+    [quickboardController setTextInputContext:context.get()];
+    [quickboardController setDelegate:self];
+
+    return quickboardController;
+}
+
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+
 - (void)presentViewControllerForCurrentFocusedElement
 {
     [self dismissAllInputViewControllers:NO];
@@ -6471,12 +6518,23 @@
         break;
     case WebKit::InputType::None:
         break;
-    default:
+    default: {
+#if HAVE(QUICKBOARD_CONTROLLER)
+        if (_page->preferences().quickboardControllerForTextInputEnabled()) {
+            _presentedQuickboardController = [self _createQuickboardController:presentingViewController];
+            break;
+        }
+#endif
         _presentedFullScreenInputViewController = adoptNS([[WKTextInputListViewController alloc] initWithDelegate:self]);
         break;
     }
+    }
 
-    ASSERT(_presentedFullScreenInputViewController);
+#if HAVE(QUICKBOARD_CONTROLLER)
+    ASSERT_IMPLIES(_presentedQuickboardController, !_presentedFullScreenInputViewController);
+    ASSERT_IMPLIES(_presentedFullScreenInputViewController, !_presentedQuickboardController);
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+    ASSERT(self._isPresentingFullScreenInputView);
     ASSERT(presentingViewController);
 
     if (!prefersModalPresentation && [presentingViewController isKindOfClass:[UINavigationController class]])
@@ -6484,19 +6542,33 @@
     else
         _inputNavigationViewControllerForFullScreenInputs = nil;
 
-    // Present the input view controller on an existing navigation stack, if possible. If there is no navigation stack we can use, fall back to presenting modally.
-    // This is because the HI specification (for certain scenarios) calls for navigation-style view controller presentation, but WKWebView can't make any guarantees
-    // about clients' view controller hierarchies, so we can only try our best to avoid presenting modally. Clients can implicitly opt in to specced behavior by using
-    // UINavigationController to present the web view.
-    if (_inputNavigationViewControllerForFullScreenInputs)
-        [_inputNavigationViewControllerForFullScreenInputs pushViewController:_presentedFullScreenInputViewController.get() animated:YES];
-    else
-        [presentingViewController presentViewController:_presentedFullScreenInputViewController.get() animated:YES completion:nil];
+    RetainPtr<UIViewController> controller;
+    if (_presentedFullScreenInputViewController) {
+        // Present the input view controller on an existing navigation stack, if possible. If there is no navigation stack we can use, fall back to presenting modally.
+        // This is because the HI specification (for certain scenarios) calls for navigation-style view controller presentation, but WKWebView can't make any guarantees
+        // about clients' view controller hierarchies, so we can only try our best to avoid presenting modally. Clients can implicitly opt in to specced behavior by using
+        // UINavigationController to present the web view.
+        if (_inputNavigationViewControllerForFullScreenInputs)
+            [_inputNavigationViewControllerForFullScreenInputs pushViewController:_presentedFullScreenInputViewController.get() animated:YES];
+        else
+            [presentingViewController presentViewController:_presentedFullScreenInputViewController.get() animated:YES completion:nil];
+        controller = _presentedFullScreenInputViewController.get();
+    }
 
+#if HAVE(QUICKBOARD_CONTROLLER)
+    if (_presentedQuickboardController) {
+        [_presentedQuickboardController present];
+        controller = [presentingViewController presentedViewController];
+    }
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+
     // Presenting a fullscreen input view controller fully obscures the web view. Without taking this token, the web content process will get backgrounded.
     _page->process().startBackgroundActivityForFullscreenInput();
 
-    [presentingViewController.transitionCoordinator animateAlongsideTransition:nil completion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), controller = _presentedFullScreenInputViewController] (id <UIViewControllerTransitionCoordinatorContext>) {
+    // FIXME: PUICQuickboardController does not present its view controller immediately, since it asynchronously
+    // establishes a connection to QuickboardViewService before presenting the remote view controller.
+    // Fixing this requires a version of `-[PUICQuickboardController present]` that takes a completion handler.
+    [presentingViewController.transitionCoordinator animateAlongsideTransition:nil completion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), controller] (id <UIViewControllerTransitionCoordinatorContext>) {
         auto strongWebView = weakWebView.get();
         id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]);
         if ([uiDelegate respondsToSelector:@selector(_webView:didPresentFocusedElementViewController:)])
@@ -6504,26 +6576,50 @@
     }];
 }
 
+- (BOOL)_isPresentingFullScreenInputView
+{
+#if HAVE(QUICKBOARD_CONTROLLER)
+    if (_presentedQuickboardController)
+        return YES;
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+    return _presentedFullScreenInputViewController;
+}
+
 - (void)dismissAllInputViewControllers:(BOOL)animated
 {
-    auto navigationController = WTFMove(_inputNavigationViewControllerForFullScreenInputs);
-    auto presentedController = WTFMove(_presentedFullScreenInputViewController);
+    auto navigationController = std::exchange(_inputNavigationViewControllerForFullScreenInputs, nil);
 
-    if (!presentedController)
+    if (!self._isPresentingFullScreenInputView) {
+        ASSERT(!navigationController);
         return;
+    }
 
-    if ([navigationController viewControllers].lastObject == presentedController.get())
-        [navigationController popViewControllerAnimated:animated];
-    else
-        [presentedController dismissViewControllerAnimated:animated completion:nil];
+    if (auto controller = std::exchange(_presentedFullScreenInputViewController, nil)) {
+        if ([navigationController viewControllers].lastObject == controller.get())
+            [navigationController popViewControllerAnimated:animated];
+        else
+            [controller dismissViewControllerAnimated:animated completion:nil];
 
-    [[presentedController transitionCoordinator] animateAlongsideTransition:nil completion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), controller = presentedController] (id <UIViewControllerTransitionCoordinatorContext>) {
-        auto strongWebView = weakWebView.get();
-        id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]);
-        if ([uiDelegate respondsToSelector:@selector(_webView:didDismissFocusedElementViewController:)])
-            [uiDelegate _webView:strongWebView.get() didDismissFocusedElementViewController:controller.get()];
-    }];
+        [[controller transitionCoordinator] animateAlongsideTransition:nil completion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), controller] (id <UIViewControllerTransitionCoordinatorContext>) {
+            auto strongWebView = weakWebView.get();
+            id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]);
+            if ([uiDelegate respondsToSelector:@selector(_webView:didDismissFocusedElementViewController:)])
+                [uiDelegate _webView:strongWebView.get() didDismissFocusedElementViewController:controller.get()];
+        }];
+    }
 
+#if HAVE(QUICKBOARD_CONTROLLER)
+    if (auto controller = std::exchange(_presentedQuickboardController, nil)) {
+        auto presentedViewController = retainPtr([controller quickboardPresentingViewController].presentedViewController);
+        [controller dismissWithCompletion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), presentedViewController] {
+            auto strongWebView = weakWebView.get();
+            id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]);
+            if ([uiDelegate respondsToSelector:@selector(_webView:didDismissFocusedElementViewController:)])
+                [uiDelegate _webView:strongWebView.get() didDismissFocusedElementViewController:presentedViewController.get()];
+        }];
+    }
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+
     if (_shouldRestoreFirstResponderStatusAfterLosingFocus) {
         _shouldRestoreFirstResponderStatusAfterLosingFocus = NO;
         if (!self.isFirstResponder)
@@ -6689,6 +6785,23 @@
     return self.focusedSelectElementOptions[index].isSelected;
 }
 
+#if HAVE(QUICKBOARD_CONTROLLER)
+
+#pragma mark - PUICQuickboardControllerDelegate
+
+- (void)quickboardController:(PUICQuickboardController *)controller textInputValueDidChange:(NSAttributedString *)attributedText
+{
+    _page->setTextAsync(attributedText.string);
+    [self dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:controller];
+}
+
+- (void)quickboardControllerTextInputValueCancelled:(PUICQuickboardController *)controller
+{
+    [self dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:controller];
+}
+
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+
 #endif // PLATFORM(WATCHOS)
 
 - (void)_wheelChangedWithEvent:(UIEvent *)event
@@ -8641,7 +8754,7 @@
 
 #if PLATFORM(WATCHOS)
 
-- (void)dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:(PUICQuickboardViewController *)quickboard
+- (void)dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:(id)controller
 {
     BOOL shouldRevealFocusOverlay = NO;
     // In the case where there's nothing the user could potentially do besides dismiss the overlay, we can just automatically without asking the delegate.
@@ -8662,14 +8775,42 @@
     } else
         _page->blurFocusedElement();
 
+    bool shouldDismissViewController = [controller isKindOfClass:UIViewController.class]
+#if HAVE(QUICKBOARD_CONTROLLER)
+        && !_presentedQuickboardController
+#endif
+        && controller != _presentedFullScreenInputViewController;
     // The Quickboard view controller passed into this delegate method is not necessarily the view controller we originally presented;
     // this happens in the case when the user chooses an input method (e.g. scribble) and a new Quickboard view controller is presented.
-    if (quickboard != _presentedFullScreenInputViewController)
-        [quickboard dismissViewControllerAnimated:YES completion:nil];
+    if (shouldDismissViewController)
+        [(UIViewController *)controller dismissViewControllerAnimated:YES completion:nil];
 
-    [self dismissAllInputViewControllers:quickboard == _presentedFullScreenInputViewController];
+    [self dismissAllInputViewControllers:controller == _presentedFullScreenInputViewController];
 }
 
+- (UITextContentType)textContentTypeForQuickboard
+{
+    switch (_focusedElementInformation.elementType) {
+    case WebKit::InputType::Password:
+        return UITextContentTypePassword;
+    case WebKit::InputType::URL:
+        return UITextContentTypeURL;
+    case WebKit::InputType::Email:
+        return UITextContentTypeEmailAddress;
+    case WebKit::InputType::Phone:
+        return UITextContentTypeTelephoneNumber;
+    default:
+        // The element type alone is insufficient to infer content type; fall back to autofill data.
+        if (auto contentType = contentTypeFromFieldName(_focusedElementInformation.autofillFieldName))
+            return contentType;
+
+        if (_focusedElementInformation.isAutofillableUsernameField)
+            return UITextContentTypeUsername;
+
+        return nil;
+    }
+}
+
 #pragma mark - PUICQuickboardViewControllerDelegate
 
 - (void)quickboard:(PUICQuickboardViewController *)quickboard textEntered:(NSAttributedString *)attributedText
@@ -8765,25 +8906,7 @@
 
 - (NSString *)textContentTypeForListViewController:(WKTextInputListViewController *)controller
 {
-    switch (_focusedElementInformation.elementType) {
-    case WebKit::InputType::Password:
-        return UITextContentTypePassword;
-    case WebKit::InputType::URL:
-        return UITextContentTypeURL;
-    case WebKit::InputType::Email:
-        return UITextContentTypeEmailAddress;
-    case WebKit::InputType::Phone:
-        return UITextContentTypeTelephoneNumber;
-    default:
-        // The element type alone is insufficient to infer content type; fall back to autofill data.
-        if (NSString *contentType = contentTypeFromFieldName(_focusedElementInformation.autofillFieldName))
-            return contentType;
-
-        if (_focusedElementInformation.isAutofillableUsernameField)
-            return UITextContentTypeUsername;
-
-        return nil;
-    }
+    return self.textContentTypeForQuickboard;
 }
 
 - (NSArray<UITextSuggestion *> *)textSuggestionsForListViewController:(WKTextInputListViewController *)controller
@@ -9281,11 +9404,23 @@
 - (void)_simulateTextEntered:(NSString *)text
 {
 #if PLATFORM(WATCHOS)
-    if ([_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]])
+    if ([_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]]) {
         [(WKTextInputListViewController *)_presentedFullScreenInputViewController.get() enterText:text];
-#else
+        return;
+    }
+
+#if HAVE(QUICKBOARD_CONTROLLER)
+    if (_presentedQuickboardController) {
+        id <PUICQuickboardControllerDelegate> delegate = [_presentedQuickboardController delegate];
+        ASSERT(delegate == self);
+        auto string = adoptNS([[NSAttributedString alloc] initWithString:text]);
+        [delegate quickboardController:_presentedQuickboardController.get() textInputValueDidChange:string.get()];
+        return;
+    }
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+#endif // PLATFORM(WATCHOS)
+
     [self insertText:text];
-#endif
 }
 
 - (void)_simulateElementAction:(_WKElementActionType)actionType atLocation:(CGPoint)location
@@ -9338,7 +9473,11 @@
 #if PLATFORM(WATCHOS)
     if ([_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]])
         return [self textContentTypeForListViewController:(WKTextInputListViewController *)_presentedFullScreenInputViewController.get()];
-#endif
+#if HAVE(QUICKBOARD_CONTROLLER)
+    if (_presentedQuickboardController)
+        return [_presentedQuickboardController textInputContext].textContentType;
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+#endif // PLATFORM(WATCHOS)
     return self.textInputTraits.textContentType;
 }
 
@@ -9353,10 +9492,10 @@
 - (NSString *)formInputLabel
 {
 #if PLATFORM(WATCHOS)
-    if (_presentedFullScreenInputViewController)
-        return [self inputLabelTextForViewController:(id)_presentedFullScreenInputViewController.get()];
+    return [self inputLabelTextForViewController:_presentedFullScreenInputViewController.get()];
+#else
+    return nil;
 #endif
-    return nil;
 }
 
 - (void)setTimePickerValueToHour:(NSInteger)hour minute:(NSInteger)minute

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKTextInputListViewController.mm (272658 => 272659)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKTextInputListViewController.mm	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKTextInputListViewController.mm	2021-02-10 17:07:58 UTC (rev 272659)
@@ -66,7 +66,9 @@
 
     UIImage *numberPadButtonIcon = [PUICResources imageNamed:@"QuickboardAddPhoneNumber" inBundle:[NSBundle bundleWithIdentifier:@"com.apple.PepperUICore"] shouldCache:YES];
 
+    ALLOW_DEPRECATED_DECLARATIONS_BEGIN
     auto numberPadButton = adoptNS([[PUICQuickboardListTrayButton alloc] initWithFrame:CGRectZero tintColor:nil defaultHeight:self.specs.defaultButtonHeight]);
+    ALLOW_DEPRECATED_DECLARATIONS_END
     [numberPadButton setImage:numberPadButtonIcon forState:UIControlStateNormal];
     [numberPadButton addTarget:self action:@selector(presentNumberPadViewController) forControlEvents:UIControlEventTouchUpInside];
     return @[ numberPadButton.get() ];

Modified: trunk/Tools/ChangeLog (272658 => 272659)


--- trunk/Tools/ChangeLog	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Tools/ChangeLog	2021-02-10 17:07:58 UTC (rev 272659)
@@ -1,3 +1,31 @@
+2021-02-10  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [watchOS] Adopt PUICQuickboardController for text input
+        https://bugs.webkit.org/show_bug.cgi?id=221649
+
+        Reviewed by Tim Horton.
+
+        Make some small tweaks to WebKitTestRunner in support of using `PUICQuickboardController` for text input.
+
+        * WebKitTestRunner/Configurations/Base.xcconfig:
+        * WebKitTestRunner/Configurations/WebKitTestRunnerApp.xcconfig:
+
+        Adjust the test harness on watchOS to link against PepperUICore, so that we can reference the
+        `PUICQuickboardViewController` and `PUICQuickboardRemoteViewController` classes below.
+
+        * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+        * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+        (isQuickboardViewController):
+        (-[TestRunnerWKWebView _didPresentViewController:]):
+
+        Override `-presentViewController:animated:completion:` on the view controller below, and call this new method
+        on the `TestRunnerWKWebView` when a view controller is finished presenting. We need to do this to ensure that
+        the input view presentation callbacks continue to work after adopting `PUICQuickboardController`; see WebKit
+        ChangeLog for more details.
+
+        * WebKitTestRunner/ios/PlatformWebViewIOS.mm:
+        (-[PlatformWebViewController presentViewController:animated:completion:]):
+
 2021-02-10  Aakash Jain  <aakash_j...@apple.com>
 
         [ews] Add build-step to run build.webkit.org new unit-tests

Modified: trunk/Tools/WebKitTestRunner/Configurations/Base.xcconfig (272658 => 272659)


--- trunk/Tools/WebKitTestRunner/Configurations/Base.xcconfig	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Tools/WebKitTestRunner/Configurations/Base.xcconfig	2021-02-10 17:07:58 UTC (rev 272659)
@@ -136,4 +136,4 @@
 
 HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR)/usr/local/include $(BUILT_PRODUCTS_DIR)/WebCoreTestSupport $(WEBCORE_PRIVATE_HEADERS_DIR)/ForwardingHeaders $(NEXT_ROOT)/usr/local/include/WebCoreTestSupport $(HEADER_SEARCH_PATHS_$(WK_COCOA_TOUCH));
 HEADER_SEARCH_PATHS_ = ;
-HEADER_SEARCH_PATHS_cocoatouch = $(SRCROOT)/../../Source/WebKit/Platform/spi/ios;
+HEADER_SEARCH_PATHS_cocoatouch = $(SRCROOT)/../../Source/WebKit/Platform/spi/ios $(SRCROOT)/../../Source/WebKit/Platform/spi/watchos;

Modified: trunk/Tools/WebKitTestRunner/Configurations/WebKitTestRunnerApp.xcconfig (272658 => 272659)


--- trunk/Tools/WebKitTestRunner/Configurations/WebKitTestRunnerApp.xcconfig	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Tools/WebKitTestRunner/Configurations/WebKitTestRunnerApp.xcconfig	2021-02-10 17:07:58 UTC (rev 272659)
@@ -31,6 +31,7 @@
 GCC_ENABLE_OBJC_EXCEPTIONS = YES;
 
 OTHER_LDFLAGS = $(inherited) -lWebKitTestRunner -lWebCoreTestSupport -framework _javascript_Core -framework CoreGraphics -framework QuartzCore -framework ImageIO -framework IOKit -framework UIKit -framework WebKit -framework Foundation -framework GraphicsServices;
+OTHER_LDFLAGS[sdk=watch*] = $(OTHER_LDFLAGS) -framework PepperUICore
 
 STRIP_STYLE = debugging;
 

Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h (272658 => 272659)


--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h	2021-02-10 17:07:58 UTC (rev 272659)
@@ -54,6 +54,7 @@
 - (void)immediatelyDismissContextMenuIfNeeded;
 - (void)installCustomMenuAction:(NSString *)name dismissesAutomatically:(BOOL)dismissesAutomatically callback:(dispatch_block_t)callback;
 
+- (void)_didPresentViewController:(UIViewController *)viewController;
 - (void)zoomToScale:(double)scale animated:(BOOL)animated completionHandler:(void (^)(void))completionHandler;
 - (void)accessibilityRetrieveSpeakSelectionContentWithCompletionHandler:(void (^)(void))completionHandler;
 - (void)_didEndRotation;

Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm (272658 => 272659)


--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm	2021-02-10 17:07:58 UTC (rev 272659)
@@ -49,6 +49,10 @@
 @end
 #endif
 
+#if PLATFORM(WATCHOS)
+#import "PepperUICoreSPI.h"
+#endif
+
 struct CustomMenuActionInfo {
     RetainPtr<NSString> name;
     BOOL dismissesAutomatically { NO };
@@ -525,6 +529,25 @@
     return [self valueForKeyPath:@"_currentContentView"];
 }
 
+static bool isQuickboardViewController(UIViewController *viewController)
+{
+#if PLATFORM(WATCHOS)
+    if ([viewController isKindOfClass:PUICQuickboardViewController.class])
+        return true;
+#if HAVE(QUICKBOARD_CONTROLLER)
+    if ([viewController isKindOfClass:PUICQuickboardRemoteViewController.class])
+        return true;
+#endif // HAVE(QUICKBOARD_CONTROLLER)
+#endif // PLATFORM(WATCHOS)
+    return false;
+}
+
+- (void)_didPresentViewController:(UIViewController *)viewController
+{
+    if (isQuickboardViewController(viewController))
+        [self _invokeShowKeyboardCallbackIfNecessary];
+}
+
 #pragma mark - WKUIDelegatePrivate
 
 // In extra zoom mode, fullscreen form control UI takes on the same role as keyboards and input view controllers

Modified: trunk/Tools/WebKitTestRunner/ios/PlatformWebViewIOS.mm (272658 => 272659)


--- trunk/Tools/WebKitTestRunner/ios/PlatformWebViewIOS.mm	2021-02-10 17:06:57 UTC (rev 272658)
+++ trunk/Tools/WebKitTestRunner/ios/PlatformWebViewIOS.mm	2021-02-10 17:07:58 UTC (rev 272659)
@@ -36,6 +36,7 @@
 #import <WebKit/WKWebViewPrivate.h>
 #import <pal/spi/cocoa/QuartzCoreSPI.h>
 #import <wtf/BlockObjCExceptions.h>
+#import <wtf/BlockPtr.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/Vector.h>
 #import <wtf/WeakObjCPtr.h>
@@ -175,6 +176,19 @@
     }];
 }
 
+- (void)presentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void(^)(void))completion
+{
+    auto weakWebView = WeakObjCPtr<TestRunnerWKWebView>(WTR::TestController::singleton().mainWebView()->platformView());
+    [super presentViewController:viewController animated:animated completion:[weakWebView, completion = makeBlockPtr(completion), viewController = retainPtr(viewController)] {
+        if (completion)
+            completion();
+
+        auto strongWebView = weakWebView.get();
+        if (WTR::TestController::singleton().mainWebView()->platformView() == strongWebView)
+            [strongWebView _didPresentViewController:viewController.get()];
+    }];
+}
+
 @end
 
 namespace WTR {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to