Make !ENABLE(DATA_DETECTION) iOS build actually succeed
[WebKit-https.git] / Source / WebKit / UIProcess / ios / WKContentViewInteraction.mm
index beb4735..c176a05 100644 (file)
@@ -40,6 +40,7 @@
 #import "TextInputSPI.h"
 #import "UIKitSPI.h"
 #import "WKActionSheetAssistant.h"
+#import "WKDatePickerViewController.h"
 #import "WKError.h"
 #import "WKFocusedFormControlViewController.h"
 #import "WKFormInputControl.h"
@@ -53,6 +54,7 @@
 #import "WKPreviewElementInfoInternal.h"
 #import "WKSelectMenuViewController.h"
 #import "WKTextInputViewController.h"
+#import "WKTimePickerViewController.h"
 #import "WKUIDelegatePrivate.h"
 #import "WKWebViewConfiguration.h"
 #import "WKWebViewConfigurationPrivate.h"
@@ -274,7 +276,7 @@ const CGFloat minimumTapHighlightRadius = 2.0;
 
 @interface WKFormInputSession : NSObject <_WKFormInputSession>
 
-- (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo;
+- (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo requiresStrongPasswordAssistance:(BOOL)requiresStrongPasswordAssistance;
 - (void)invalidate;
 
 @end
@@ -286,15 +288,17 @@ const CGFloat minimumTapHighlightRadius = 2.0;
     RetainPtr<NSArray<UITextSuggestion *>> _suggestions;
     BOOL _accessoryViewShouldNotShow;
     BOOL _forceSecureTextEntry;
+    BOOL _requiresStrongPasswordAssistance;
 }
 
-- (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo
+- (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo requiresStrongPasswordAssistance:(BOOL)requiresStrongPasswordAssistance
 {
     if (!(self = [super init]))
         return nil;
 
     _contentView = view;
     _focusedElementInfo = elementInfo;
+    _requiresStrongPasswordAssistance = requiresStrongPasswordAssistance;
 
     return self;
 }
@@ -383,6 +387,11 @@ const CGFloat minimumTapHighlightRadius = 2.0;
     [suggestionDelegate setSuggestions:suggestions];
 }
 
+- (BOOL)requiresStrongPasswordAssistance
+{
+    return _requiresStrongPasswordAssistance;
+}
+
 - (void)invalidate
 {
     id <UITextInputSuggestionDelegate> suggestionDelegate = (id <UITextInputSuggestionDelegate>)_contentView.inputDelegate;
@@ -658,6 +667,8 @@ static WKDragSessionContext *ensureLocalDragSessionContext(id <UIDragSession> se
     [_highlightView removeFromSuperview];
     _outstandingPositionInformationRequest = std::nullopt;
 
+    _focusRequiresStrongPasswordAssistance = NO;
+
     if (_interactionViewsContainerView) {
         [self.layer removeObserver:self forKeyPath:@"transform"];
         [_interactionViewsContainerView removeFromSuperview];
@@ -715,6 +726,7 @@ static WKDragSessionContext *ensureLocalDragSessionContext(id <UIDragSession> se
     }
     
     _inputViewUpdateDeferrer = nullptr;
+    _assistedNodeInformation = { };
 }
 
 - (void)_removeDefaultGestureRecognizers
@@ -761,9 +773,9 @@ static WKDragSessionContext *ensureLocalDragSessionContext(id <UIDragSession> se
 
 
 #pragma mark - UITextAutoscrolling
-- (void)startAutoscroll:(CGPoint)point
+- (void)startAutoscroll:(CGPoint)pointInDocument
 {
-    _page->startAutoscrollAtPosition(point);
+    _page->startAutoscrollAtPosition(pointInDocument);
 }
 
 - (void)cancelAutoscroll
@@ -916,7 +928,6 @@ static WKDragSessionContext *ensureLocalDragSessionContext(id <UIDragSession> se
     // and do nothing if the return value is NO.
 
     _resigningFirstResponder = YES;
-
     if (!_webView->_activeFocusedStateRetainCount) {
         // We need to complete the editing operation before we blur the element.
         [_inputPeripheral endEditing];
@@ -3132,7 +3143,7 @@ static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoi
 
 - (UITextRange *)selectedTextRange
 {
-    if (_page->editorState().selectionIsNone)
+    if (_page->editorState().selectionIsNone || _page->editorState().isMissingPostLayoutData)
         return nil;
     auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
     FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
@@ -4020,6 +4031,10 @@ static bool isAssistableInputType(InputType type)
     if (_assistedNodeInformation.elementType == information.elementType && _assistedNodeInformation.elementRect == information.elementRect)
         return;
 
+    _focusRequiresStrongPasswordAssistance = NO;
+    if ([inputDelegate respondsToSelector:@selector(_webView:focusRequiresStrongPasswordAssistance:)])
+        _focusRequiresStrongPasswordAssistance = [inputDelegate _webView:_webView focusRequiresStrongPasswordAssistance:focusedElementInfo.get()];
+
     BOOL editableChanged = [self setIsEditable:YES];
     _assistedNodeInformation = information;
     _inputPeripheral = nil;
@@ -4031,7 +4046,7 @@ static bool isAssistableInputType(InputType type)
 #if ENABLE(EXTRA_ZOOM_MODE)
     [self presentFocusedFormControlViewController:NO];
     if (!_isChangingFocus)
-        [self presentViewControllerForAssistedNode:_assistedNodeInformation];
+        [self presentViewControllerForCurrentAssistedNode];
 #else
     [self reloadInputViews];
 #endif
@@ -4063,7 +4078,7 @@ static bool isAssistableInputType(InputType type)
     [_inputPeripheral beginEditing];
 
     if ([inputDelegate respondsToSelector:@selector(_webView:didStartInputSession:)]) {
-        _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self focusedElementInfo:focusedElementInfo.get()]);
+        _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self focusedElementInfo:focusedElementInfo.get() requiresStrongPasswordAssistance:_focusRequiresStrongPasswordAssistance]);
         [inputDelegate _webView:_webView didStartInputSession:_formInputSession.get()];
     }
     
@@ -4079,6 +4094,7 @@ static bool isAssistableInputType(InputType type)
 
     _assistedNodeInformation.elementType = InputType::None;
     _inputPeripheral = nil;
+    _focusRequiresStrongPasswordAssistance = NO;
 
     [self _stopAssistingKeyboard];
     [_formAccessoryView hideAutoFillButton];
@@ -4088,9 +4104,7 @@ static bool isAssistableInputType(InputType type)
     [_webSelectionAssistant resignedFirstResponder];
 
 #if ENABLE(EXTRA_ZOOM_MODE)
-    [self dismissTextInputViewController:YES];
-    [self dismissNumberPadViewController:YES];
-    [self dismissSelectMenuViewController:YES];
+    [self dismissAllInputViewControllers];
     if (!_isChangingFocus)
         [self dismissFocusedFormControlViewController:[_focusedFormControlViewController isVisible]];
 #endif
@@ -4102,8 +4116,71 @@ static bool isAssistableInputType(InputType type)
     [_webView didEndFormControlInteraction];
 }
 
+- (void)updateCurrentAssistedNodeInformation:(Function<void(bool didUpdate)>&&)callback
+{
+    WeakObjCPtr<WKContentView> weakSelf { self };
+    auto identifierBeforeUpdate = _assistedNodeInformation.assistedNodeIdentifier;
+    _page->requestAssistedNodeInformation([callback = WTFMove(callback), identifierBeforeUpdate, weakSelf] (auto& info, auto error) {
+        if (!weakSelf || error != CallbackBase::Error::None || info.assistedNodeIdentifier != identifierBeforeUpdate) {
+            // If the assisted node may have changed in the meantime, don't overwrite assisted node information.
+            callback(false);
+            return;
+        }
+
+        weakSelf.get()->_assistedNodeInformation = info;
+        callback(true);
+    });
+}
+
 #if ENABLE(EXTRA_ZOOM_MODE)
 
+- (void)dismissAllInputViewControllers
+{
+    [self dismissTextInputViewController:YES];
+    [self dismissNumberPadViewController:YES];
+    [self dismissSelectMenuViewController:YES];
+    [self dismissTimePickerViewController:YES];
+    [self dismissDatePickerViewController:YES];
+}
+
+- (void)presentDatePickerViewController:(BOOL)animated
+{
+    if (_datePickerViewController)
+        return;
+
+    _datePickerViewController = adoptNS([[WKDatePickerViewController alloc] initWithText:_assistedNodeInformation.value textSuggestions:@[ ]]);
+    [_datePickerViewController setDelegate:self];
+    [_focusedFormControlViewController presentViewController:_datePickerViewController.get() animated:animated completion:nil];
+}
+
+- (void)dismissDatePickerViewController:(BOOL)animated
+{
+    if (!_datePickerViewController)
+        return;
+
+    auto datePickerViewController = WTFMove(_datePickerViewController);
+    [datePickerViewController dismissViewControllerAnimated:animated completion:nil];
+}
+
+- (void)presentTimePickerViewController:(BOOL)animated
+{
+    if (_timePickerViewController)
+        return;
+
+    _timePickerViewController = adoptNS([[WKTimePickerViewController alloc] initWithText:_assistedNodeInformation.value textSuggestions:@[ ]]);
+    [_timePickerViewController setDelegate:self];
+    [_focusedFormControlViewController presentViewController:_timePickerViewController.get() animated:animated completion:nil];
+}
+
+- (void)dismissTimePickerViewController:(BOOL)animated
+{
+    if (!_timePickerViewController)
+        return;
+
+    auto timePickerViewController = WTFMove(_timePickerViewController);
+    [timePickerViewController dismissViewControllerAnimated:animated completion:nil];
+}
+
 - (void)presentSelectMenuViewController:(BOOL)animated
 {
     if (_selectMenuViewController)
@@ -4131,6 +4208,7 @@ static bool isAssistableInputType(InputType type)
     _focusedFormControlViewController = adoptNS([[WKFocusedFormControlViewController alloc] init]);
     [_focusedFormControlViewController setDelegate:self];
     [[UIViewController _viewControllerForFullScreenPresentationFromView:self] presentViewController:_focusedFormControlViewController.get() animated:animated completion:nil];
+    [self setInputDelegate:_focusedFormControlViewController.get()];
 }
 
 - (void)dismissNumberPadViewController:(BOOL)animated
@@ -4149,6 +4227,7 @@ static bool isAssistableInputType(InputType type)
 
     [_focusedFormControlViewController dismissViewControllerAnimated:animated completion:nil];
     _focusedFormControlViewController = nil;
+    [self setInputDelegate:nil];
 }
 
 - (void)presentNumberPadViewController:(BOOL)animated
@@ -4161,18 +4240,11 @@ static bool isAssistableInputType(InputType type)
     [_focusedFormControlViewController presentViewController:_numberPadViewController.get() animated:animated completion:nil];
 }
 
-- (void)presentViewControllerForAssistedNode:(const AssistedNodeInformation&)info
+- (void)presentViewControllerForCurrentAssistedNode
 {
-    switch (info.elementType) {
-    case InputType::ContentEditable:
-    case InputType::Text:
-    case InputType::Password:
-    case InputType::TextArea:
-    case InputType::Search:
-    case InputType::Email:
-    case InputType::URL:
-        [self presentTextInputViewController:YES];
-        break;
+    [self dismissAllInputViewControllers];
+
+    switch (_assistedNodeInformation.elementType) {
     case InputType::Number:
     case InputType::NumberPad:
     case InputType::Phone:
@@ -4181,7 +4253,16 @@ static bool isAssistableInputType(InputType type)
     case InputType::Select:
         [self presentSelectMenuViewController:YES];
         break;
+    case InputType::Time:
+        [self presentTimePickerViewController:YES];
+        break;
+    case InputType::Date:
+        [self presentDatePickerViewController:YES];
+        break;
+    case InputType::None:
+        break;
     default:
+        [self presentTextInputViewController:YES];
         break;
     }
 }
@@ -4194,6 +4275,8 @@ static bool isAssistableInputType(InputType type)
     _textInputViewController = adoptNS([[WKTextInputViewController alloc] initWithText:_assistedNodeInformation.value textSuggestions:@[ ]]);
     [_textInputViewController setDelegate:self];
     [_focusedFormControlViewController presentViewController:_textInputViewController.get() animated:animated completion:nil];
+
+    [_textInputViewController setSuggestions:[_focusedFormControlViewController suggestions]];
 }
 
 - (void)dismissTextInputViewController:(BOOL)animated
@@ -4207,18 +4290,17 @@ static bool isAssistableInputType(InputType type)
 
 - (void)textInputController:(WKTextFormControlViewController *)controller didCommitText:(NSString *)text
 {
-    // FIXME: Update cached AssistedNodeInformation state in the UI process.
-    _page->setTextAsync(text);
+    [self textInputController:controller didCommitText:text withSuggestion:nil];
 }
 
-- (void)textInputController:(WKTextFormControlViewController *)controller didRequestDismissalWithAction:(WKFormControlAction)action
+- (void)textInputController:(WKTextFormControlViewController *)controller didCommitText:(NSString *)text withSuggestion:(UITextSuggestion *)suggestion
 {
-    if (action == WKFormControlActionCancel) {
-        _page->blurAssistedNode();
-        return;
-    }
+    if (suggestion)
+        [self insertTextSuggestion:suggestion];
+    else
+        _page->setTextAsync(text);
 
-    if (_assistedNodeInformation.formAction.isEmpty() && !_assistedNodeInformation.hasNextNode && !_assistedNodeInformation.hasPreviousNode) {
+    if (![self actionNameForFocusedFormControlController:_focusedFormControlViewController.get()] && !_assistedNodeInformation.hasNextNode && !_assistedNodeInformation.hasPreviousNode) {
         // In this case, there's no point in collapsing down to the form control focus UI because there's nothing the user could potentially do
         // besides dismiss the UI, so we just automatically dismiss the focused form control UI.
         _page->blurAssistedNode();
@@ -4226,8 +4308,16 @@ static bool isAssistableInputType(InputType type)
     }
 
     [_focusedFormControlViewController show:NO];
-    [self dismissTextInputViewController:YES];
-    [self dismissNumberPadViewController:YES];
+    [self dismissAllInputViewControllers];
+    [self updateCurrentAssistedNodeInformation:[weakSelf = WeakObjCPtr<WKContentView>(self)] (bool didUpdate) {
+        if (didUpdate)
+            [weakSelf.get()->_focusedFormControlViewController reloadData:YES];
+    }];
+}
+
+- (void)textInputControllerDidRequestDismissal:(WKTextFormControlViewController *)controller
+{
+    _page->blurAssistedNode();
 }
 
 - (void)focusedFormControlControllerDidSubmit:(WKFocusedFormControlViewController *)controller
@@ -4243,7 +4333,10 @@ static bool isAssistableInputType(InputType type)
 
 - (void)focusedFormControlControllerDidBeginEditing:(WKFocusedFormControlViewController *)controller
 {
-    [self presentViewControllerForAssistedNode:_assistedNodeInformation];
+    [self updateCurrentAssistedNodeInformation:[weakSelf = WeakObjCPtr<WKContentView>(self)] (bool didUpdate) {
+        if (didUpdate)
+            [weakSelf presentViewControllerForCurrentAssistedNode];
+    }];
 }
 
 - (CGRect)highlightedRectForFocusedFormControlController:(WKFocusedFormControlViewController *)controller inCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace
@@ -4256,7 +4349,16 @@ static bool isAssistableInputType(InputType type)
     if (_assistedNodeInformation.formAction.isEmpty())
         return nil;
 
-    return _assistedNodeInformation.elementType == InputType::Search ? formControlSearchButtonTitle() : formControlGoButtonTitle();
+    switch (_assistedNodeInformation.elementType) {
+    case InputType::Select:
+    case InputType::Time:
+    case InputType::Date:
+        return nil;
+    case InputType::Search:
+        return formControlSearchButtonTitle();
+    default:
+        return formControlGoButtonTitle();
+    }
 }
 
 - (void)focusedFormControlControllerDidRequestNextNode:(WKFocusedFormControlViewController *)controller
@@ -4281,6 +4383,11 @@ static bool isAssistableInputType(InputType type)
     return _assistedNodeInformation.hasPreviousNode;
 }
 
+- (void)focusedFormControllerDidUpdateSuggestions:(WKFocusedFormControlViewController *)controller
+{
+    [_textInputViewController setSuggestions:controller.suggestions];
+}
+
 #pragma mark - WKSelectMenuViewControllerDelegate
 
 - (void)selectMenu:(WKSelectMenuViewController *)selectMenu didSelectItemAtIndex:(NSUInteger)index
@@ -4359,6 +4466,30 @@ static bool isAssistableInputType(InputType type)
 
 #endif // ENABLE(EXTRA_ZOOM_MODE)
 
+- (void)_wheelChangedWithEvent:(UIEvent *)event
+{
+#if ENABLE(EXTRA_ZOOM_MODE)
+    if ([_numberPadViewController handleWheelEvent:event])
+        return;
+
+    if ([_textInputViewController handleWheelEvent:event])
+        return;
+
+    if ([_selectMenuViewController handleWheelEvent:event])
+        return;
+
+    if ([_timePickerViewController handleWheelEvent:event])
+        return;
+
+    if ([_datePickerViewController handleWheelEvent:event])
+        return;
+
+    if ([_focusedFormControlViewController handleWheelEvent:event])
+        return;
+#endif
+    [super _wheelChangedWithEvent:event];
+}
+
 - (void)_selectionChanged
 {
     _selectionNeedsUpdate = YES;
@@ -5005,14 +5136,18 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
 
 - (NSDictionary *)_autofillContext
 {
-    if (_assistedNodeInformation.elementType == InputType::None || !_assistedNodeInformation.acceptsAutofilledLoginCredentials)
+    BOOL provideStrongPasswordAssistance = _focusRequiresStrongPasswordAssistance && _assistedNodeInformation.elementType == InputType::Password;
+    if (_assistedNodeInformation.elementType == InputType::None || (!_assistedNodeInformation.acceptsAutofilledLoginCredentials && !provideStrongPasswordAssistance))
         return nil;
 
+    if (provideStrongPasswordAssistance)
+        return @{ @"_automaticPasswordKeyboard" : @YES };
+
     NSURL *platformURL = _assistedNodeInformation.representingPageURL;
-    if (!platformURL)
-        return nil;
+    if (platformURL)
+        return @{ @"_WebViewURL" : platformURL };
 
-    return @{ @"_WebViewURL" : platformURL };
+    return nil;
 }
 
 #pragma mark - UIDragInteractionDelegate
@@ -5419,8 +5554,10 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
             return NO;
         if (linkURL.protocolIsInHTTPFamily())
             return YES;
+#if ENABLE(DATA_DETECTION)
         if (DataDetection::canBePresentedByDataDetectors(linkURL))
             return YES;
+#endif
         return NO;
     }
     return YES;
@@ -5439,6 +5576,10 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
     BOOL supportsAttachmentPreview = ([uiDelegate respondsToSelector:@selector(_attachmentListForWebView:)] || respondsToAttachmentListForWebViewSourceIsManaged)
         && [uiDelegate respondsToSelector:@selector(_webView:indexIntoAttachmentListForElement:)];
     BOOL canShowAttachmentPreview = (_positionInformation.isAttachment || _positionInformation.isImage) && supportsAttachmentPreview;
+    BOOL isDataDetectorLink = NO;
+#if ENABLE(DATA_DETECTION)
+    isDataDetectorLink = _positionInformation.isDataDetectorLink;
+#endif
 
     if (canShowImagePreview && _positionInformation.isAnimatedImage) {
         canShowImagePreview = NO;
@@ -5450,7 +5591,7 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
         return nil;
 
     const URL& linkURL = _positionInformation.url;
-    if (!useImageURLForLink && (linkURL.isEmpty() || (!linkURL.protocolIsInHTTPFamily() && !_positionInformation.isDataDetectorLink))) {
+    if (!useImageURLForLink && (linkURL.isEmpty() || (!linkURL.protocolIsInHTTPFamily() && !isDataDetectorLink))) {
         if (canShowLinkPreview && !canShowImagePreview)
             return nil;
         canShowLinkPreview = NO;
@@ -5463,7 +5604,8 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
             dataForPreview[UIPreviewDataLink] = (NSURL *)_positionInformation.imageURL;
         else
             dataForPreview[UIPreviewDataLink] = (NSURL *)linkURL;
-        if (_positionInformation.isDataDetectorLink) {
+#if ENABLE(DATA_DETECTION)
+        if (isDataDetectorLink) {
             NSDictionary *context = nil;
             if ([uiDelegate respondsToSelector:@selector(_dataDetectionContextForWebView:)])
                 context = [uiDelegate _dataDetectionContextForWebView:_webView];
@@ -5487,6 +5629,7 @@ static NSArray<UIItemProvider *> *extractItemProvidersFromDropSession(id <UIDrop
             if (newContext)
                 dataForPreview[UIPreviewDataDDContext] = newContext;
         }
+#endif // ENABLE(DATA_DETECTION)
     } else if (canShowImagePreview) {
         *type = UIPreviewItemTypeImage;
         dataForPreview[UIPreviewDataLink] = (NSURL *)_positionInformation.imageURL;