| File: | Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm |
| Warning: | line 4285, column 1 The return value is +1 and results in a memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (C) 2012-2024 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 | * THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #import "config.h" |
| 27 | #import "WKContentViewInteraction.h" |
| 28 | |
| 29 | #if PLATFORM(IOS_FAMILY)(defined 1 && 1) |
| 30 | |
| 31 | #import "APIUIClient.h" |
| 32 | #import "CocoaImage.h" |
| 33 | #import "CompletionHandlerCallChecker.h" |
| 34 | #import "DefaultWebBrowserChecks.h" |
| 35 | #import "DocumentEditingContext.h" |
| 36 | #import "ImageAnalysisUtilities.h" |
| 37 | #import "InsertTextOptions.h" |
| 38 | #import "KeyEventInterpretationContext.h" |
| 39 | #import "Logging.h" |
| 40 | #import "NativeWebKeyboardEvent.h" |
| 41 | #import "NativeWebTouchEvent.h" |
| 42 | #import "NetworkProcessMessages.h" |
| 43 | #import "PDFDisplayMode.h" |
| 44 | #import "PageClient.h" |
| 45 | #import "PickerDismissalReason.h" |
| 46 | #import "PlatformWritingToolsUtilities.h" |
| 47 | #import "RemoteLayerTreeDrawingAreaProxy.h" |
| 48 | #import "RemoteLayerTreeViews.h" |
| 49 | #import "RemoteScrollingCoordinatorProxyIOS.h" |
| 50 | #import "RevealItem.h" |
| 51 | #import "SmartMagnificationController.h" |
| 52 | #import "TextChecker.h" |
| 53 | #import "TextCheckerState.h" |
| 54 | #import "TextInputSPI.h" |
| 55 | #import "TextRecognitionUpdateResult.h" |
| 56 | #import "UIGamepadProvider.h" |
| 57 | #import "UIKitSPI.h" |
| 58 | #import "UIKitUtilities.h" |
| 59 | #import "WKActionSheetAssistant.h" |
| 60 | #import "WKContextMenuElementInfoInternal.h" |
| 61 | #import "WKContextMenuElementInfoPrivate.h" |
| 62 | #import "WKDatePickerViewController.h" |
| 63 | #import "WKDateTimeInputControl.h" |
| 64 | #import "WKError.h" |
| 65 | #import "WKExtendedTextInputTraits.h" |
| 66 | #import "WKFocusedFormControlView.h" |
| 67 | #import "WKFormColorControl.h" |
| 68 | #import "WKFormSelectControl.h" |
| 69 | #import "WKFrameInfoInternal.h" |
| 70 | #import "WKHighlightLongPressGestureRecognizer.h" |
| 71 | #import "WKImageAnalysisGestureRecognizer.h" |
| 72 | #import "WKImagePreviewViewController.h" |
| 73 | #import "WKInspectorNodeSearchGestureRecognizer.h" |
| 74 | #import "WKNSURLExtras.h" |
| 75 | #import "WKPreviewActionItemIdentifiers.h" |
| 76 | #import "WKPreviewActionItemInternal.h" |
| 77 | #import "WKPreviewElementInfoInternal.h" |
| 78 | #import "WKQuickboardViewControllerDelegate.h" |
| 79 | #import "WKScrollView.h" |
| 80 | #import "WKSelectMenuListViewController.h" |
| 81 | #import "WKSyntheticFlagsChangedWebEvent.h" |
| 82 | #import "WKTapHighlightView.h" |
| 83 | #import "WKTextInputListViewController.h" |
| 84 | #import "WKTextInteractionWrapper.h" |
| 85 | #import "WKTextPlaceholder.h" |
| 86 | #import "WKTextSelectionRect.h" |
| 87 | #import "WKTimePickerViewController.h" |
| 88 | #import "WKTouchEventsGestureRecognizer.h" |
| 89 | #import "WKUIDelegatePrivate.h" |
| 90 | #import "WKWebViewConfiguration.h" |
| 91 | #import "WKWebViewConfigurationPrivate.h" |
| 92 | #import "WKWebViewIOS.h" |
| 93 | #import "WKWebViewInternal.h" |
| 94 | #import "WKWebViewPrivate.h" |
| 95 | #import "WKWebViewPrivateForTesting.h" |
| 96 | #import "WebAutocorrectionContext.h" |
| 97 | #import "WebAutocorrectionData.h" |
| 98 | #import "WebDataListSuggestionsDropdownIOS.h" |
| 99 | #import "WebEvent.h" |
| 100 | #import "WebFoundTextRange.h" |
| 101 | #import "WebIOSEventFactory.h" |
| 102 | #import "WebMouseEvent.h" |
| 103 | #import "WebPageMessages.h" |
| 104 | #import "WebPageProxy.h" |
| 105 | #import "WebPageProxyMessages.h" |
| 106 | #import "WebProcessProxy.h" |
| 107 | #import "_WKActivatedElementInfoInternal.h" |
| 108 | #import "_WKDragActionsInternal.h" |
| 109 | #import "_WKElementAction.h" |
| 110 | #import "_WKElementActionInternal.h" |
| 111 | #import "_WKFocusedElementInfo.h" |
| 112 | #import "_WKInputDelegate.h" |
| 113 | #import "_WKTextInputContextInternal.h" |
| 114 | #import <CoreText/CTFont.h> |
| 115 | #import <CoreText/CTFontDescriptor.h> |
| 116 | #import <MobileCoreServices/UTCoreTypes.h> |
| 117 | #import <UniformTypeIdentifiers/UTCoreTypes.h> |
| 118 | #import <WebCore/AppHighlight.h> |
| 119 | #import <WebCore/ColorCocoa.h> |
| 120 | #import <WebCore/ColorSerialization.h> |
| 121 | #import <WebCore/CompositionHighlight.h> |
| 122 | #import <WebCore/DOMPasteAccess.h> |
| 123 | #import <WebCore/DataDetection.h> |
| 124 | #import <WebCore/FloatQuad.h> |
| 125 | #import <WebCore/FloatRect.h> |
| 126 | #import <WebCore/FontAttributeChanges.h> |
| 127 | #import <WebCore/InputMode.h> |
| 128 | #import <WebCore/KeyEventCodesIOS.h> |
| 129 | #import <WebCore/KeyboardScroll.h> |
| 130 | #import <WebCore/LocalizedStrings.h> |
| 131 | #import <WebCore/MIMETypeRegistry.h> |
| 132 | #import <WebCore/NodeIdentifier.h> |
| 133 | #import <WebCore/NotImplemented.h> |
| 134 | #import <WebCore/Pasteboard.h> |
| 135 | #import <WebCore/Path.h> |
| 136 | #import <WebCore/PathUtilities.h> |
| 137 | #import <WebCore/PlatformLayerIdentifier.h> |
| 138 | #import <WebCore/PlatformTextAlternativesBETextAlternatives.h> |
| 139 | #import <WebCore/PromisedAttachmentInfo.h> |
| 140 | #import <WebCore/ScrollTypes.h> |
| 141 | #import <WebCore/Scrollbar.h> |
| 142 | #import <WebCore/ShareData.h> |
| 143 | #import <WebCore/TextAlternativeWithRange.h> |
| 144 | #import <WebCore/TextAnimationTypes.h> |
| 145 | #import <WebCore/TextIndicator.h> |
| 146 | #import <WebCore/TextRecognitionResult.h> |
| 147 | #import <WebCore/TouchAction.h> |
| 148 | #import <WebCore/UTIRegistry.h> |
| 149 | #import <WebCore/UTIUtilities.h> |
| 150 | #import <WebCore/VisibleSelection.h> |
| 151 | #import <WebCore/WebCoreCALayerExtras.h> |
| 152 | #import <WebCore/WebEventPrivate.h> |
| 153 | #import <WebCore/WebTextIndicatorLayer.h> |
| 154 | #import <WebCore/WindowsKeyboardCodes.h> |
| 155 | #import <WebCore/WritingDirection.h> |
| 156 | #import <WebKit/WebSelectionRect.h> // FIXME: WebKit should not include WebKitLegacy headers! |
| 157 | #import <pal/spi/cg/CoreGraphicsSPI.h> |
| 158 | #import <pal/spi/cocoa/DataDetectorsCoreSPI.h> |
| 159 | #import <pal/spi/cocoa/LaunchServicesSPI.h> |
| 160 | #import <pal/spi/cocoa/NSAttributedStringSPI.h> |
| 161 | #import <pal/spi/cocoa/QuartzCoreSPI.h> |
| 162 | #import <pal/spi/ios/BarcodeSupportSPI.h> |
| 163 | #import <pal/spi/ios/GraphicsServicesSPI.h> |
| 164 | #import <pal/spi/ios/ManagedConfigurationSPI.h> |
| 165 | #import <pal/system/ios/UserInterfaceIdiom.h> |
| 166 | #import <ranges> |
| 167 | #import <wtf/BlockObjCExceptions.h> |
| 168 | #import <wtf/BlockPtr.h> |
| 169 | #import <wtf/CallbackAggregator.h> |
| 170 | #import <wtf/RuntimeApplicationChecks.h> |
| 171 | #import <wtf/Scope.h> |
| 172 | #import <wtf/SetForScope.h> |
| 173 | #import <wtf/StdLibExtras.h> |
| 174 | #import <wtf/SystemFree.h> |
| 175 | #import <wtf/WeakObjCPtr.h> |
| 176 | #import <wtf/cocoa/NSURLExtras.h> |
| 177 | #import <wtf/cocoa/RuntimeApplicationChecksCocoa.h> |
| 178 | #import <wtf/cocoa/TypeCastsCocoa.h> |
| 179 | #import <wtf/cocoa/VectorCocoa.h> |
| 180 | #import <wtf/text/MakeString.h> |
| 181 | #import <wtf/text/TextStream.h> |
| 182 | |
| 183 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 184 | #import <WebCore/DragData.h> |
| 185 | #import <WebCore/DragItem.h> |
| 186 | #import <WebCore/PlatformPasteboard.h> |
| 187 | #import <WebCore/WebItemProviderPasteboard.h> |
| 188 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 189 | #import "ModelPresentationManagerProxy.h" |
| 190 | #endif |
| 191 | #endif |
| 192 | |
| 193 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 194 | #import <UIKit/_UILookupGestureRecognizer.h> |
| 195 | #endif |
| 196 | |
| 197 | #if ENABLE(ATTACHMENT_ELEMENT)(defined 1 && 1) |
| 198 | #import "APIAttachment.h" |
| 199 | #endif |
| 200 | |
| 201 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 202 | #import "PepperUICoreSPI.h" |
| 203 | #endif |
| 204 | |
| 205 | #if HAVE(AVKIT)(defined 1 && 1) |
| 206 | #import <pal/spi/cocoa/AVKitSPI.h> |
| 207 | #endif |
| 208 | |
| 209 | #if ENABLE(WEB_AUTHN)(defined 1 && 1) |
| 210 | #import "WKDigitalCredentialsPicker.h" |
| 211 | #endif |
| 212 | |
| 213 | #if ENABLE(WRITING_TOOLS)(defined 1 && 1) |
| 214 | #import "WKTextAnimationManagerIOS.h" |
| 215 | #endif |
| 216 | |
| 217 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) || ENABLE(WRITING_TOOLS)(defined 1 && 1) |
| 218 | #import "WebKitSwiftSoftLink.h" |
| 219 | #endif |
| 220 | |
| 221 | #import <pal/cocoa/VisionKitCoreSoftLink.h> |
| 222 | #import <pal/cocoa/TranslationUIServicesSoftLink.h> |
| 223 | #import <pal/ios/ManagedConfigurationSoftLink.h> |
| 224 | #import <pal/ios/QuickLookSoftLink.h> |
| 225 | #import <pal/spi/ios/DataDetectorsUISoftLink.h> |
| 226 | |
| 227 | #if HAVE(AUTOCORRECTION_ENHANCEMENTS)(defined 1 && 1) |
| 228 | #define UIWKDocumentRequestAutocorrectedRanges(1 << 7) (1 << 7) |
| 229 | #endif |
| 230 | |
| 231 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 232 | |
| 233 | @interface WKUITextSelectionRect : UITextSelectionRect |
| 234 | + (instancetype)selectionRectWithCGRect:(CGRect)rect; |
| 235 | @end |
| 236 | |
| 237 | @implementation WKUITextSelectionRect { |
| 238 | CGRect _selectionRect; |
| 239 | } |
| 240 | |
| 241 | + (instancetype)selectionRectWithCGRect:(CGRect)rect |
| 242 | { |
| 243 | return adoptNS([[WKUITextSelectionRect alloc] initWithCGRect:rect]).autorelease(); |
| 244 | } |
| 245 | |
| 246 | - (instancetype)initWithCGRect:(CGRect)rect |
| 247 | { |
| 248 | if (self = [super init]) |
| 249 | _selectionRect = rect; |
| 250 | return self; |
| 251 | } |
| 252 | |
| 253 | - (CGRect)rect |
| 254 | { |
| 255 | return _selectionRect; |
| 256 | } |
| 257 | |
| 258 | @end |
| 259 | |
| 260 | #endif // USE(BROWSERENGINEKIT) |
| 261 | |
| 262 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) && USE(UICONTEXTMENU)(defined 1 && 1) |
| 263 | static NSString * const webkitShowLinkPreviewsPreferenceKey = @"WebKitShowLinkPreviews"; |
| 264 | #endif |
| 265 | |
| 266 | #if HAVE(UI_POINTER_INTERACTION)(defined 1 && 1) |
| 267 | static NSString * const pointerRegionIdentifier = @"WKPointerRegion"; |
| 268 | static NSString * const editablePointerRegionIdentifier = @"WKEditablePointerRegion"; |
| 269 | |
| 270 | @interface WKContentView (WKUIPointerInteractionDelegate) <UIPointerInteractionDelegate> |
| 271 | @end |
| 272 | #endif |
| 273 | |
| 274 | #if HAVE(PENCILKIT_TEXT_INPUT)(defined 1 && 1) |
| 275 | @interface WKContentView (WKUIIndirectScribbleInteractionDelegate) <UIIndirectScribbleInteractionDelegate> |
| 276 | @end |
| 277 | #endif |
| 278 | |
| 279 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 280 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 281 | @interface WKContentView (QuickboardControllerSupport) <PUICQuickboardControllerDelegate> |
| 282 | @end |
| 283 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 284 | |
| 285 | @interface WKContentView (WatchSupport) <WKFocusedFormControlViewDelegate, WKSelectMenuListViewControllerDelegate, WKTextInputListViewControllerDelegate> |
| 286 | @end |
| 287 | #endif // HAVE(PEPPER_UI_CORE) |
| 288 | |
| 289 | static void *WKContentViewKVOTransformContext = &WKContentViewKVOTransformContext; |
| 290 | |
| 291 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 292 | |
| 293 | @interface WKContentView (ImageAnalysis) <WKImageAnalysisGestureRecognizerDelegate> |
| 294 | @end |
| 295 | |
| 296 | #if USE(QUICK_LOOK)(defined 1 && 1) |
| 297 | |
| 298 | @interface WKContentView (ImageAnalysisPreview) <QLPreviewControllerDelegate, QLPreviewControllerDataSource, QLPreviewItemDataProvider> |
| 299 | @end |
| 300 | |
| 301 | #endif // USE(QUICK_LOOK) |
| 302 | |
| 303 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 304 | |
| 305 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 306 | |
| 307 | @interface WKContentView (ImageAnalysisInteraction) <VKCImageAnalysisInteractionDelegate> |
| 308 | @end |
| 309 | |
| 310 | #endif |
| 311 | |
| 312 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 313 | |
| 314 | static bool canAttemptTextRecognitionForNonImageElements(const WebKit::InteractionInformationAtPosition& information, const WebKit::WebPreferences& preferences) |
| 315 | { |
| 316 | return preferences.textRecognitionInVideosEnabled() && information.isPausedVideo; |
| 317 | } |
| 318 | |
| 319 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 320 | |
| 321 | @interface AVPlayerViewController (Staging_86237428) |
| 322 | - (void)setImageAnalysis:(CocoaImageAnalysis *)analysis; |
| 323 | @end |
| 324 | |
| 325 | #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| 326 | |
| 327 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 328 | |
| 329 | #if HAVE(UI_TEXT_SELECTION_DISPLAY_INTERACTION)(defined 1 && 1) |
| 330 | |
| 331 | @interface WKContentView (UITextSelectionDisplayInteraction) <UITextSelectionDisplayInteractionDelegate> |
| 332 | @end |
| 333 | |
| 334 | #endif |
| 335 | |
| 336 | namespace WebKit { |
| 337 | using namespace WebCore; |
| 338 | |
| 339 | enum class IgnoreTapGestureReason : uint8_t { |
| 340 | None, |
| 341 | ToggleEditMenu, |
| 342 | DeferToScrollView, |
| 343 | }; |
| 344 | |
| 345 | static NSArray<NSString *> *supportedPlainTextPasteboardTypes() |
| 346 | { |
| 347 | static NeverDestroyed supportedTypes = [] { |
| 348 | auto types = adoptNS([[NSMutableArray alloc] init]); |
| 349 | [types addObject:@(WebCore::PasteboardCustomData::cocoaType().characters())]; |
| 350 | [types addObject:UTTypeURL.identifier]; |
| 351 | [types addObjectsFromArray:UIPasteboardTypeListString]; |
| 352 | return types; |
| 353 | }(); |
| 354 | return supportedTypes.get().get(); |
| 355 | } |
| 356 | |
| 357 | static NSArray<NSString *> *supportedRichTextPasteboardTypesForPasteConfiguration() |
| 358 | { |
| 359 | static NeverDestroyed supportedTypes = [] { |
| 360 | auto types = adoptNS([[NSMutableArray alloc] init]); |
| 361 | [types addObject:UTTypeWebArchive.identifier]; |
| 362 | [types addObjectsFromArray:UIPasteboardTypeListImage]; |
| 363 | [types addObjectsFromArray:protect(supportedPlainTextPasteboardTypes()).get()]; |
| 364 | return types; |
| 365 | }(); |
| 366 | return supportedTypes.get().get(); |
| 367 | } |
| 368 | |
| 369 | static NSArray<NSString *> *supportedRichTextPasteboardTypes() |
| 370 | { |
| 371 | static NeverDestroyed supportedTypes = [] { |
| 372 | auto types = adoptNS([[NSMutableArray alloc] init]); |
| 373 | [types addObject:protect(WebCore::WebArchivePboardType).get()]; |
| 374 | [types addObjectsFromArray:protect(supportedRichTextPasteboardTypesForPasteConfiguration()).get()]; |
| 375 | return types; |
| 376 | }(); |
| 377 | return supportedTypes.get().get(); |
| 378 | } |
| 379 | |
| 380 | #if HAVE(UI_PASTE_CONFIGURATION)(defined 1 && 1) |
| 381 | |
| 382 | static NSArray<NSString *> *supportedRichTextPasteboardTypesWithAttachmentsForPasteConfiguration() |
| 383 | { |
| 384 | static NeverDestroyed supportedTypes = [] { |
| 385 | auto types = adoptNS([[NSMutableArray alloc] init]); |
| 386 | [types addObjectsFromArray:protect(supportedRichTextPasteboardTypesForPasteConfiguration()).get()]; |
| 387 | [types addObjectsFromArray:protect(WebCore::Pasteboard::supportedFileUploadPasteboardTypes()).get()]; |
| 388 | return types; |
| 389 | }(); |
| 390 | return supportedTypes.get().get(); |
| 391 | } |
| 392 | |
| 393 | #endif // HAVE(UI_PASTE_CONFIGURATION) |
| 394 | |
| 395 | WKSelectionDrawingInfo::WKSelectionDrawingInfo() |
| 396 | : type(WebCore::SelectionType::None) |
| 397 | { |
| 398 | } |
| 399 | |
| 400 | WKSelectionDrawingInfo::WKSelectionDrawingInfo(const EditorState& editorState) |
| 401 | { |
| 402 | if (editorState.selectionType == WebCore::SelectionType::None || (editorState.selectionType == WebCore::SelectionType::Caret && !editorState.isContentEditable)) { |
| 403 | type = WebCore::SelectionType::None; |
| 404 | return; |
| 405 | } |
| 406 | |
| 407 | type = WebCore::SelectionType::Range; |
| 408 | if (!editorState.postLayoutData) |
| 409 | return; |
| 410 | |
| 411 | auto& postLayoutData = *editorState.postLayoutData; |
| 412 | auto& visualData = *editorState.visualData; |
| 413 | caretRect = visualData.caretRectAtEnd; |
| 414 | caretColor = postLayoutData.caretColor; |
| 415 | selectionGeometries = visualData.selectionGeometries; |
| 416 | selectionClipRect = visualData.selectionClipRect; |
| 417 | enclosingLayerID = visualData.enclosingLayerID; |
| 418 | } |
| 419 | |
| 420 | auto WKSelectionDrawingInfo::compare(const WKSelectionDrawingInfo& other) const -> ComparisonResult |
| 421 | { |
| 422 | using enum ComparisonResult; |
| 423 | |
| 424 | if (type != other.type) |
| 425 | return VisuallyDistinct; |
| 426 | |
| 427 | if (type == WebCore::SelectionType::Range) { |
| 428 | if (caretRect != other.caretRect) |
| 429 | return VisuallyDistinct; |
| 430 | |
| 431 | if (caretColor != other.caretColor) |
| 432 | return VisuallyDistinct; |
| 433 | |
| 434 | if (selectionGeometries.size() != other.selectionGeometries.size()) |
| 435 | return VisuallyDistinct; |
| 436 | |
| 437 | for (unsigned i = 0; i < selectionGeometries.size(); ++i) { |
| 438 | auto& aGeometry = selectionGeometries[i]; |
| 439 | auto& bGeometry = other.selectionGeometries[i]; |
| 440 | auto behavior = aGeometry.behavior(); |
| 441 | if (behavior != bGeometry.behavior()) |
| 442 | return VisuallyDistinct; |
| 443 | |
| 444 | if (behavior == WebCore::SelectionRenderingBehavior::CoalesceBoundingRects && aGeometry.rect() != bGeometry.rect()) |
| 445 | return VisuallyDistinct; |
| 446 | |
| 447 | if (behavior == WebCore::SelectionRenderingBehavior::UseIndividualQuads && aGeometry.quad() != bGeometry.quad()) |
| 448 | return VisuallyDistinct; |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | if (type != WebCore::SelectionType::None && selectionClipRect != other.selectionClipRect) |
| 453 | return VisuallyDistinct; |
| 454 | |
| 455 | if (enclosingLayerID != other.enclosingLayerID) |
| 456 | return EquivalentExceptForEnclosingLayer; |
| 457 | |
| 458 | return EquivalentIncludingEnclosingLayer; |
| 459 | } |
| 460 | |
| 461 | TextStream& operator<<(TextStream& stream, const WKSelectionDrawingInfo& info) |
| 462 | { |
| 463 | TextStream::GroupScope group(stream); |
| 464 | stream.dumpProperty("type"_s, std::to_underlying(info.type)); |
| 465 | stream.dumpProperty("caret rect"_s, info.caretRect); |
| 466 | stream.dumpProperty("caret color"_s, info.caretColor); |
| 467 | stream.dumpProperty("selection geometries"_s, info.selectionGeometries); |
| 468 | stream.dumpProperty("selection clip rect"_s, info.selectionClipRect); |
| 469 | stream.dumpProperty("layer"_s, info.enclosingLayerID); |
| 470 | return stream; |
| 471 | } |
| 472 | |
| 473 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 474 | |
| 475 | class ImageAnalysisGestureDeferralToken final : public RefCounted<ImageAnalysisGestureDeferralToken> { |
| 476 | public: |
| 477 | static RefPtr<ImageAnalysisGestureDeferralToken> create(WKContentView *view) |
| 478 | { |
| 479 | return adoptRef(*new ImageAnalysisGestureDeferralToken(view)); |
| 480 | } |
| 481 | |
| 482 | ~ImageAnalysisGestureDeferralToken() |
| 483 | { |
| 484 | if (auto view = m_view.get()) |
| 485 | [view _endImageAnalysisGestureDeferral:m_shouldPreventTextSelection ? WebKit::ShouldPreventGestures::Yes : WebKit::ShouldPreventGestures::No]; |
| 486 | } |
| 487 | |
| 488 | void setShouldPreventTextSelection() |
| 489 | { |
| 490 | m_shouldPreventTextSelection = true; |
| 491 | } |
| 492 | |
| 493 | private: |
| 494 | ImageAnalysisGestureDeferralToken(WKContentView *view) |
| 495 | : m_view(view) |
| 496 | { |
| 497 | } |
| 498 | |
| 499 | WeakObjCPtr<WKContentView> m_view; |
| 500 | bool m_shouldPreventTextSelection { false }; |
| 501 | }; |
| 502 | |
| 503 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 504 | |
| 505 | enum class TextPositionAnchor : uint8_t { |
| 506 | Start = 1 << 0, |
| 507 | End = 1 << 1 |
| 508 | }; |
| 509 | |
| 510 | enum class NotifyInputDelegate : bool { No, Yes }; |
| 511 | |
| 512 | } // namespace WebKit |
| 513 | |
| 514 | @interface WKRelativeTextPosition : UITextPosition |
| 515 | - (instancetype)initWithAnchors:(OptionSet<WebKit::TextPositionAnchor>)start offset:(NSInteger)offset; |
| 516 | @property (nonatomic, readonly) OptionSet<WebKit::TextPositionAnchor> anchors; |
| 517 | @property (nonatomic, readonly, getter=isRelativeToStart) BOOL relativeToStart; |
| 518 | @property (nonatomic, readonly) NSInteger offset; |
| 519 | @end |
| 520 | |
| 521 | @interface WKRelativeTextRange : UITextRange |
| 522 | - (instancetype)initWithStart:(UITextPosition *)start end:(UITextPosition *)end; |
| 523 | @property (nonatomic, readonly) WKRelativeTextPosition *start; |
| 524 | @property (nonatomic, readonly) WKRelativeTextPosition *end; |
| 525 | @end |
| 526 | |
| 527 | constexpr float highlightDelay = 0.12; |
| 528 | constexpr float tapAndHoldDelay = 0.75; |
| 529 | constexpr CGFloat minimumTapHighlightRadius = 2.0; |
| 530 | constexpr double fasterTapSignificantZoomThreshold = 0.8; |
| 531 | |
| 532 | @interface WKTextRange : UITextRange { |
| 533 | CGRect _startRect; |
| 534 | CGRect _endRect; |
| 535 | BOOL _isNone; |
| 536 | BOOL _isRange; |
| 537 | BOOL _isEditable; |
| 538 | RetainPtr<NSArray<WKTextSelectionRect *>> _selectionRects; |
| 539 | NSUInteger _selectedTextLength; |
| 540 | } |
| 541 | @property (nonatomic) CGRect startRect; |
| 542 | @property (nonatomic) CGRect endRect; |
| 543 | @property (nonatomic) BOOL isNone; |
| 544 | @property (nonatomic) BOOL isRange; |
| 545 | @property (nonatomic) BOOL isEditable; |
| 546 | @property (nonatomic) NSUInteger selectedTextLength; |
| 547 | @property (copy, nonatomic) NSArray<WKTextSelectionRect *> *selectionRects; |
| 548 | |
| 549 | + (WKTextRange *)textRangeWithState:(BOOL)isNone isRange:(BOOL)isRange isEditable:(BOOL)isEditable startRect:(CGRect)startRect endRect:(CGRect)endRect selectionRects:(NSArray *)selectionRects selectedTextLength:(NSUInteger)selectedTextLength; |
| 550 | |
| 551 | @end |
| 552 | |
| 553 | @interface WKTextPosition : UITextPosition |
| 554 | |
| 555 | @property (nonatomic) CGRect positionRect; |
| 556 | @property (nonatomic) OptionSet<WebKit::TextPositionAnchor> anchors; |
| 557 | |
| 558 | + (WKTextPosition *)textPositionWithRect:(CGRect)positionRect; |
| 559 | |
| 560 | @end |
| 561 | |
| 562 | #if HAVE(UIFINDINTERACTION)(defined 1 && 1) |
| 563 | |
| 564 | @interface WKFoundTextRange : UITextRange |
| 565 | |
| 566 | @property (nonatomic) NSUInteger order; |
| 567 | @property (nonatomic, copy) NSArray *pathToFrame; |
| 568 | |
| 569 | + (WKFoundTextRange *)foundTextRangeWithWebFoundTextRange:(WebKit::WebFoundTextRange)range; |
| 570 | |
| 571 | - (WebKit::WebFoundTextRange)webFoundTextRange; |
| 572 | |
| 573 | @end |
| 574 | |
| 575 | @interface WKFoundTextPosition : UITextPosition |
| 576 | |
| 577 | @property (nonatomic) NSUInteger order; |
| 578 | |
| 579 | @end |
| 580 | |
| 581 | @interface WKFoundDOMTextRange : WKFoundTextRange |
| 582 | |
| 583 | @property (nonatomic) NSUInteger location; |
| 584 | @property (nonatomic) NSUInteger length; |
| 585 | |
| 586 | @end |
| 587 | |
| 588 | @interface WKFoundDOMTextPosition : WKFoundTextPosition |
| 589 | |
| 590 | @property (nonatomic) NSUInteger offset; |
| 591 | |
| 592 | + (WKFoundDOMTextPosition *)textPositionWithOffset:(NSUInteger)offset order:(NSUInteger)order; |
| 593 | |
| 594 | @end |
| 595 | |
| 596 | @interface WKFoundPDFTextRange : WKFoundTextRange |
| 597 | |
| 598 | @property (nonatomic) NSUInteger startPage; |
| 599 | @property (nonatomic) NSUInteger startPageOffset; |
| 600 | @property (nonatomic) NSUInteger endPage; |
| 601 | @property (nonatomic) NSUInteger endPageOffset; |
| 602 | |
| 603 | @end |
| 604 | |
| 605 | @interface WKFoundPDFTextPosition : WKFoundTextPosition |
| 606 | |
| 607 | @property (nonatomic) NSUInteger page; |
| 608 | @property (nonatomic) NSUInteger offset; |
| 609 | |
| 610 | + (WKFoundPDFTextPosition *)textPositionWithPage:(NSUInteger)page offset:(NSUInteger)offset; |
| 611 | |
| 612 | @end |
| 613 | |
| 614 | #endif |
| 615 | |
| 616 | inline static UITextPosition *positionWithOffsetFrom(UITextPosition *position, NSInteger offset) |
| 617 | { |
| 618 | if (!offset) |
| 619 | return position; |
| 620 | |
| 621 | if (auto concretePosition = dynamic_objc_cast<WKTextPosition>(position); !concretePosition.anchors.isEmpty()) |
| 622 | return adoptNS([[WKRelativeTextPosition alloc] initWithAnchors:concretePosition.anchors offset:offset]).autorelease(); |
| 623 | |
| 624 | if (auto relativePosition = dynamic_objc_cast<WKRelativeTextPosition>(position)) |
| 625 | return adoptNS([[WKRelativeTextPosition alloc] initWithAnchors:[relativePosition anchors] offset:offset + [relativePosition offset]]).autorelease(); |
| 626 | |
| 627 | return nilnullptr; |
| 628 | } |
| 629 | |
| 630 | inline static std::pair<OptionSet<WebKit::TextPositionAnchor>, NSInteger> anchorsAndOffset(UITextPosition *position) |
| 631 | { |
| 632 | if (auto concretePosition = dynamic_objc_cast<WKTextPosition>(position); concretePosition.anchors) |
| 633 | return { concretePosition.anchors, 0 }; |
| 634 | |
| 635 | if (auto relativePosition = dynamic_objc_cast<WKRelativeTextPosition>(position)) |
| 636 | return { relativePosition.anchors, relativePosition.offset }; |
| 637 | |
| 638 | return { { }, 0 }; |
| 639 | } |
| 640 | |
| 641 | inline static RetainPtr<NSString> textRelativeToSelectionStart(WKRelativeTextRange *range, const WebKit::EditorState::PostLayoutData& data, std::optional<char32_t> lastInsertedCharacterOverride) |
| 642 | { |
| 643 | auto start = range.start; |
| 644 | auto end = range.end; |
| 645 | if (!start || !end) |
| 646 | return nilnullptr; |
| 647 | |
| 648 | if (!start.relativeToStart || !end.relativeToStart) |
| 649 | return nilnullptr; |
| 650 | |
| 651 | auto startOffset = start.offset; |
| 652 | auto endOffset = end.offset; |
| 653 | if (startOffset < -2 || endOffset > 1 || startOffset >= endOffset) |
| 654 | return nilnullptr; |
| 655 | |
| 656 | StringBuilder string; |
| 657 | string.reserveCapacity(endOffset - startOffset); |
| 658 | auto appendIfNonZero = [&string](auto character) { |
| 659 | if (character) |
| 660 | string.append(character); |
| 661 | }; |
| 662 | for (auto offset = startOffset; offset < endOffset; ++offset) { |
| 663 | switch (offset) { |
| 664 | case -2: |
| 665 | appendIfNonZero(data.twoCharacterBeforeSelection); |
| 666 | break; |
| 667 | case -1: { |
| 668 | appendIfNonZero(lastInsertedCharacterOverride.value_or(data.characterBeforeSelection)); |
| 669 | break; |
| 670 | } |
| 671 | case 0: |
| 672 | appendIfNonZero(data.characterAfterSelection); |
| 673 | break; |
| 674 | } |
| 675 | } |
| 676 | return string.createNSString(); |
| 677 | } |
| 678 | |
| 679 | @implementation WKRelativeTextPosition |
| 680 | |
| 681 | - (instancetype)initWithAnchors:(OptionSet<WebKit::TextPositionAnchor>)anchors offset:(NSInteger)offset |
| 682 | { |
| 683 | if (self = [super init]) { |
| 684 | _anchors = anchors; |
| 685 | _offset = offset; |
| 686 | } |
| 687 | return self; |
| 688 | } |
| 689 | |
| 690 | - (BOOL)isRelativeToStart |
| 691 | { |
| 692 | return _anchors.contains(WebKit::TextPositionAnchor::Start); |
| 693 | } |
| 694 | |
| 695 | - (NSString *)description |
| 696 | { |
| 697 | return [NSString stringWithFormat:@"WKRelativeTextPosition(%s, %d)", _anchors.contains(WebKit::TextPositionAnchor::Start) ? "start" : "end", static_cast<int>(_offset)]; |
| 698 | } |
| 699 | |
| 700 | @end |
| 701 | |
| 702 | @implementation WKRelativeTextRange { |
| 703 | RetainPtr<WKRelativeTextPosition> _start; |
| 704 | RetainPtr<WKRelativeTextPosition> _end; |
| 705 | } |
| 706 | |
| 707 | - (instancetype)initWithStart:(UITextPosition *)start end:(UITextPosition *)end |
| 708 | { |
| 709 | if (self = [super init]) { |
| 710 | auto [startAnchors, startOffset] = anchorsAndOffset(start); |
| 711 | auto [endAnchors, endOffset] = anchorsAndOffset(end); |
| 712 | if (!startAnchors.isEmpty() && !endAnchors.isEmpty()) { |
| 713 | _start = adoptNS([[WKRelativeTextPosition alloc] initWithAnchors:startAnchors offset:startOffset]); |
| 714 | _end = adoptNS([[WKRelativeTextPosition alloc] initWithAnchors:endAnchors offset:endOffset]); |
| 715 | } |
| 716 | } |
| 717 | return self; |
| 718 | } |
| 719 | |
| 720 | - (WKRelativeTextPosition *)start |
| 721 | { |
| 722 | return _start.get(); |
| 723 | } |
| 724 | |
| 725 | - (WKRelativeTextPosition *)end |
| 726 | { |
| 727 | return _end.get(); |
| 728 | } |
| 729 | |
| 730 | - (BOOL)isEmpty |
| 731 | { |
| 732 | return [_start anchors] == [_end anchors] && [_start offset] == [_end offset]; |
| 733 | } |
| 734 | |
| 735 | - (NSString *)description |
| 736 | { |
| 737 | return [NSString stringWithFormat:@"WKRelativeTextRange(start=%@, end=%@)", _start.get(), _end.get()]; |
| 738 | } |
| 739 | |
| 740 | @end |
| 741 | |
| 742 | @interface WKAutocorrectionRects : UIWKAutocorrectionRects |
| 743 | + (WKAutocorrectionRects *)autocorrectionRectsWithFirstCGRect:(CGRect)firstRect lastCGRect:(CGRect)lastRect; |
| 744 | @end |
| 745 | |
| 746 | @interface WKAutocorrectionContext : UIWKAutocorrectionContext |
| 747 | + (WKAutocorrectionContext *)emptyAutocorrectionContext; |
| 748 | + (WKAutocorrectionContext *)autocorrectionContextWithWebContext:(const WebKit::WebAutocorrectionContext&)context; |
| 749 | @end |
| 750 | |
| 751 | @interface UIView (UIViewInternalHack) |
| 752 | + (BOOL)_addCompletion:(void(^)(BOOL))completion; |
| 753 | @end |
| 754 | |
| 755 | @interface WKFocusedElementInfo : NSObject <_WKFocusedElementInfo> |
| 756 | - (instancetype)initWithFocusedElementInformation:(const WebKit::FocusedElementInformation&)information isUserInitiated:(BOOL)isUserInitiated webView:(WKWebView *)webView userObject:(NSObject <NSSecureCoding> *)userObject; |
| 757 | @end |
| 758 | |
| 759 | @implementation WKFormInputSession { |
| 760 | WeakObjCPtr<WKContentView> _contentView; |
| 761 | RetainPtr<WKFocusedElementInfo> _focusedElementInfo; |
| 762 | RetainPtr<UIView> _customInputView; |
| 763 | RetainPtr<UIView> _customInputAccessoryView; |
| 764 | RetainPtr<NSArray<UITextSuggestion *>> _suggestions; |
| 765 | BOOL _accessoryViewShouldNotShow; |
| 766 | BOOL _forceSecureTextEntry; |
| 767 | BOOL _requiresStrongPasswordAssistance; |
| 768 | } |
| 769 | |
| 770 | - (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo requiresStrongPasswordAssistance:(BOOL)requiresStrongPasswordAssistance |
| 771 | { |
| 772 | if (!(self = [super init])) |
| 773 | return nilnullptr; |
| 774 | |
| 775 | _contentView = view; |
| 776 | _focusedElementInfo = elementInfo; |
| 777 | _requiresStrongPasswordAssistance = requiresStrongPasswordAssistance; |
| 778 | |
| 779 | return self; |
| 780 | } |
| 781 | |
| 782 | - (id <_WKFocusedElementInfo>)focusedElementInfo |
| 783 | { |
| 784 | return _focusedElementInfo.get(); |
| 785 | } |
| 786 | |
| 787 | - (NSObject <NSSecureCoding> *)userObject |
| 788 | { |
| 789 | return [_focusedElementInfo userObject]; |
| 790 | } |
| 791 | |
| 792 | - (BOOL)isValid |
| 793 | { |
| 794 | return !!_contentView; |
| 795 | } |
| 796 | |
| 797 | - (NSString *)accessoryViewCustomButtonTitle |
| 798 | { |
| 799 | return [_contentView.get() formAccessoryView].autoFillButtonItem.title; |
| 800 | } |
| 801 | |
| 802 | - (void)setAccessoryViewCustomButtonTitle:(NSString *)title |
| 803 | { |
| 804 | if (title.length) |
| 805 | [[_contentView.get() formAccessoryView] showAutoFillButtonWithTitle:title]; |
| 806 | else |
| 807 | [[_contentView.get() formAccessoryView] hideAutoFillButton]; |
| 808 | if (!PAL::currentUserInterfaceIdiomIsSmallScreen()) |
| 809 | [_contentView.get() reloadInputViews]; |
| 810 | } |
| 811 | |
| 812 | - (BOOL)accessoryViewShouldNotShow |
| 813 | { |
| 814 | return _accessoryViewShouldNotShow; |
| 815 | } |
| 816 | |
| 817 | - (void)setAccessoryViewShouldNotShow:(BOOL)accessoryViewShouldNotShow |
| 818 | { |
| 819 | if (_accessoryViewShouldNotShow == accessoryViewShouldNotShow) |
| 820 | return; |
| 821 | |
| 822 | _accessoryViewShouldNotShow = accessoryViewShouldNotShow; |
| 823 | [_contentView.get() reloadInputViews]; |
| 824 | } |
| 825 | |
| 826 | - (BOOL)forceSecureTextEntry |
| 827 | { |
| 828 | return _forceSecureTextEntry; |
| 829 | } |
| 830 | |
| 831 | - (void)setForceSecureTextEntry:(BOOL)forceSecureTextEntry |
| 832 | { |
| 833 | if (_forceSecureTextEntry == forceSecureTextEntry) |
| 834 | return; |
| 835 | |
| 836 | _forceSecureTextEntry = forceSecureTextEntry; |
| 837 | [_contentView.get() reloadInputViews]; |
| 838 | } |
| 839 | |
| 840 | - (UIView *)customInputView |
| 841 | { |
| 842 | return _customInputView.get(); |
| 843 | } |
| 844 | |
| 845 | - (void)setCustomInputView:(UIView *)customInputView |
| 846 | { |
| 847 | if (customInputView == _customInputView) |
| 848 | return; |
| 849 | |
| 850 | _customInputView = customInputView; |
| 851 | [_contentView.get() reloadInputViews]; |
| 852 | } |
| 853 | |
| 854 | - (UIView *)customInputAccessoryView |
| 855 | { |
| 856 | return _customInputAccessoryView.get(); |
| 857 | } |
| 858 | |
| 859 | - (void)setCustomInputAccessoryView:(UIView *)customInputAccessoryView |
| 860 | { |
| 861 | if (_customInputAccessoryView == customInputAccessoryView) |
| 862 | return; |
| 863 | |
| 864 | _customInputAccessoryView = customInputAccessoryView; |
| 865 | [_contentView.get() reloadInputViews]; |
| 866 | } |
| 867 | |
| 868 | - (void)endEditing |
| 869 | { |
| 870 | if ([_customInputView conformsToProtocol:@protocol(WKFormControl)]) |
| 871 | [(id<WKFormControl>)_customInputView.get() controlEndEditing]; |
| 872 | } |
| 873 | |
| 874 | - (NSArray<UITextSuggestion *> *)suggestions |
| 875 | { |
| 876 | return _suggestions.get(); |
| 877 | } |
| 878 | |
| 879 | - (void)setSuggestions:(NSArray<UITextSuggestion *> *)suggestions |
| 880 | { |
| 881 | if (suggestions == _suggestions || [suggestions isEqualToArray:_suggestions.get()]) |
| 882 | return; |
| 883 | |
| 884 | _suggestions = adoptNS([suggestions copy]); |
| 885 | [_contentView.get() updateTextSuggestionsForInputDelegate]; |
| 886 | } |
| 887 | |
| 888 | - (BOOL)requiresStrongPasswordAssistance |
| 889 | { |
| 890 | return _requiresStrongPasswordAssistance; |
| 891 | } |
| 892 | |
| 893 | - (void)invalidate |
| 894 | { |
| 895 | RetainPtr<WKContentView> contentView = _contentView.get(); |
| 896 | _contentView = nilnullptr; |
| 897 | [contentView _provideSuggestionsToInputDelegate:nilnullptr]; |
| 898 | } |
| 899 | |
| 900 | - (void)reloadFocusedElementContextView |
| 901 | { |
| 902 | [_contentView.get() reloadContextViewForPresentedListViewController]; |
| 903 | } |
| 904 | |
| 905 | @end |
| 906 | |
| 907 | @implementation WKFocusedElementInfo { |
| 908 | WKInputType _type; |
| 909 | RetainPtr<NSString> _value; |
| 910 | BOOL _isUserInitiated; |
| 911 | RetainPtr<NSObject <NSSecureCoding>> _userObject; |
| 912 | RetainPtr<NSString> _placeholder; |
| 913 | RetainPtr<NSString> _label; |
| 914 | RetainPtr<WKFrameInfo> _frame; |
| 915 | } |
| 916 | |
| 917 | - (instancetype)initWithFocusedElementInformation:(const WebKit::FocusedElementInformation&)information isUserInitiated:(BOOL)isUserInitiated webView:(WKWebView *)webView userObject:(NSObject <NSSecureCoding> *)userObject |
| 918 | { |
| 919 | if (!(self = [super init])) |
| 920 | return nilnullptr; |
| 921 | |
| 922 | switch (information.elementType) { |
| 923 | case WebKit::InputType::ContentEditable: |
| 924 | _type = WKInputTypeContentEditable; |
| 925 | break; |
| 926 | case WebKit::InputType::Text: |
| 927 | _type = WKInputTypeText; |
| 928 | break; |
| 929 | case WebKit::InputType::Password: |
| 930 | _type = WKInputTypePassword; |
| 931 | break; |
| 932 | case WebKit::InputType::TextArea: |
| 933 | _type = WKInputTypeTextArea; |
| 934 | break; |
| 935 | case WebKit::InputType::Search: |
| 936 | _type = WKInputTypeSearch; |
| 937 | break; |
| 938 | case WebKit::InputType::Email: |
| 939 | _type = WKInputTypeEmail; |
| 940 | break; |
| 941 | case WebKit::InputType::URL: |
| 942 | _type = WKInputTypeURL; |
| 943 | break; |
| 944 | case WebKit::InputType::Phone: |
| 945 | _type = WKInputTypePhone; |
| 946 | break; |
| 947 | case WebKit::InputType::Number: |
| 948 | _type = WKInputTypeNumber; |
| 949 | break; |
| 950 | case WebKit::InputType::NumberPad: |
| 951 | _type = WKInputTypeNumberPad; |
| 952 | break; |
| 953 | case WebKit::InputType::Date: |
| 954 | _type = WKInputTypeDate; |
| 955 | break; |
| 956 | case WebKit::InputType::DateTimeLocal: |
| 957 | _type = WKInputTypeDateTimeLocal; |
| 958 | break; |
| 959 | case WebKit::InputType::Month: |
| 960 | _type = WKInputTypeMonth; |
| 961 | break; |
| 962 | case WebKit::InputType::Week: |
| 963 | _type = WKInputTypeWeek; |
| 964 | break; |
| 965 | case WebKit::InputType::Time: |
| 966 | _type = WKInputTypeTime; |
| 967 | break; |
| 968 | case WebKit::InputType::Select: |
| 969 | _type = WKInputTypeSelect; |
| 970 | break; |
| 971 | case WebKit::InputType::Drawing: |
| 972 | _type = WKInputTypeDrawing; |
| 973 | break; |
| 974 | case WebKit::InputType::Color: |
| 975 | _type = WKInputTypeColor; |
| 976 | break; |
| 977 | case WebKit::InputType::None: |
| 978 | _type = WKInputTypeNone; |
| 979 | break; |
| 980 | } |
| 981 | _value = information.value.createNSString().get(); |
| 982 | _isUserInitiated = isUserInitiated; |
| 983 | _userObject = userObject; |
| 984 | _placeholder = information.placeholder.createNSString().get(); |
| 985 | _label = information.label.createNSString().get(); |
| 986 | if (information.frame) |
| 987 | _frame = wrapper(API::FrameInfo::create(WebKit::FrameInfoData { *information.frame })); |
| 988 | return self; |
| 989 | } |
| 990 | |
| 991 | - (WKInputType)type |
| 992 | { |
| 993 | return _type; |
| 994 | } |
| 995 | |
| 996 | - (NSString *)value |
| 997 | { |
| 998 | return _value.get(); |
| 999 | } |
| 1000 | |
| 1001 | - (BOOL)isUserInitiated |
| 1002 | { |
| 1003 | return _isUserInitiated; |
| 1004 | } |
| 1005 | |
| 1006 | - (NSObject <NSSecureCoding> *)userObject |
| 1007 | { |
| 1008 | return _userObject.get(); |
| 1009 | } |
| 1010 | |
| 1011 | - (NSString *)label |
| 1012 | { |
| 1013 | return _label.get(); |
| 1014 | } |
| 1015 | |
| 1016 | - (NSString *)placeholder |
| 1017 | { |
| 1018 | return _placeholder.get(); |
| 1019 | } |
| 1020 | |
| 1021 | - (WKFrameInfo *)frame |
| 1022 | { |
| 1023 | return _frame.get(); |
| 1024 | } |
| 1025 | |
| 1026 | @end |
| 1027 | |
| 1028 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 1029 | |
| 1030 | @interface WKDragSessionContext : NSObject |
| 1031 | - (void)addTemporaryDirectory:(NSString *)temporaryDirectory; |
| 1032 | - (void)cleanUpTemporaryDirectories; |
| 1033 | @end |
| 1034 | |
| 1035 | @implementation WKDragSessionContext { |
| 1036 | RetainPtr<NSMutableArray> _temporaryDirectories; |
| 1037 | } |
| 1038 | |
| 1039 | - (void)addTemporaryDirectory:(NSString *)temporaryDirectory |
| 1040 | { |
| 1041 | if (!_temporaryDirectories) |
| 1042 | _temporaryDirectories = adoptNS([NSMutableArray new]); |
| 1043 | [_temporaryDirectories addObject:temporaryDirectory]; |
| 1044 | } |
| 1045 | |
| 1046 | - (void)cleanUpTemporaryDirectories |
| 1047 | { |
| 1048 | for (NSString *directory in _temporaryDirectories.get()) { |
| 1049 | NSError *error = nilnullptr; |
| 1050 | [[NSFileManager defaultManager] removeItemAtPath:directory error:&error]; |
| 1051 | RELEASE_LOG(DragAndDrop, "Removed temporary download directory: %@ with error: %@", directory, error)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Removed temporary download directory: %@ with error: %@"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG147" ) = "Removed temporary download directory: %@ with error: %@" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Removed temporary download directory: %@ with error: %@" , directory, error)]; _os_log_impl(&__dso_handle, _log_tmp , _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format( _os_fmt_buf, "Removed temporary download directory: %@ with error: %@" , directory, error), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 1052 | } |
| 1053 | _temporaryDirectories = nilnullptr; |
| 1054 | } |
| 1055 | |
| 1056 | @end |
| 1057 | |
| 1058 | static WKDragSessionContext *existingLocalDragSessionContext(id <UIDragSession> session) |
| 1059 | { |
| 1060 | return dynamic_objc_cast<WKDragSessionContext>(session.localContext); |
| 1061 | } |
| 1062 | |
| 1063 | static WKDragSessionContext *ensureLocalDragSessionContext(id <UIDragSession> session) |
| 1064 | { |
| 1065 | if (WKDragSessionContext *existingContext = existingLocalDragSessionContext(session)) |
| 1066 | return existingContext; |
| 1067 | |
| 1068 | if (session.localContext) { |
| 1069 | RELEASE_LOG(DragAndDrop, "Overriding existing local context: %@ on session: %@", session.localContext, session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Overriding existing local context: %@ on session: %@"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG148" ) = "Overriding existing local context: %@ on session: %@"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Overriding existing local context: %@ on session: %@", session .localContext, session)]; _os_log_impl(&__dso_handle, _log_tmp , _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format( _os_fmt_buf, "Overriding existing local context: %@ on session: %@" , session.localContext, session), (uint32_t)sizeof(_os_fmt_buf )) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 1070 | ASSERT_NOT_REACHED()((void)0); |
| 1071 | } |
| 1072 | |
| 1073 | session.localContext = adoptNS([[WKDragSessionContext alloc] init]).get(); |
| 1074 | return existingLocalDragSessionContext(session); |
| 1075 | } |
| 1076 | |
| 1077 | #endif // ENABLE(DRAG_SUPPORT) |
| 1078 | |
| 1079 | @implementation UIGestureRecognizer (WKContentViewHelpers) |
| 1080 | |
| 1081 | - (void)_wk_cancel |
| 1082 | { |
| 1083 | if (!self.enabled) |
| 1084 | return; |
| 1085 | |
| 1086 | [self setEnabled:NO__objc_no]; |
| 1087 | [self setEnabled:YES__objc_yes]; |
| 1088 | } |
| 1089 | |
| 1090 | - (BOOL)_wk_isTapAndAHalf |
| 1091 | { |
| 1092 | return [NSStringFromClass(self.class) containsString:@"TapAndAHalfRecognizer"]; |
| 1093 | } |
| 1094 | |
| 1095 | @end |
| 1096 | |
| 1097 | @interface WKTargetedPreviewContainer : UIView |
| 1098 | - (instancetype)initWithContentView:(WKContentView *)contentView NS_DESIGNATED_INITIALIZER__attribute__((objc_designated_initializer)); |
| 1099 | - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE__attribute__((unavailable)); |
| 1100 | - (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE__attribute__((unavailable)); |
| 1101 | @end |
| 1102 | |
| 1103 | @implementation WKTargetedPreviewContainer { |
| 1104 | __weak__attribute__((objc_ownership(weak))) WKContentView *_contentView; |
| 1105 | } |
| 1106 | |
| 1107 | - (instancetype)initWithContentView:(WKContentView *)contentView |
| 1108 | { |
| 1109 | if (!(self = [super initWithFrame:CGRectZero])) |
| 1110 | return nilnullptr; |
| 1111 | |
| 1112 | _contentView = contentView; |
| 1113 | return self; |
| 1114 | } |
| 1115 | |
| 1116 | - (void)_didRemoveSubview:(UIView *)subview |
| 1117 | { |
| 1118 | [super _didRemoveSubview:subview]; |
| 1119 | |
| 1120 | if (self.subviews.count) |
| 1121 | return; |
| 1122 | |
| 1123 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1124 | [protect(_contentView) _targetedPreviewContainerDidRemoveLastSubview:self]; |
| 1125 | #endif |
| 1126 | } |
| 1127 | |
| 1128 | @end |
| 1129 | |
| 1130 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1131 | |
| 1132 | @protocol UIContextMenuInteractionDelegate_LegacyAsyncSupport<NSObject> |
| 1133 | - (void)_contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location completion:(void(^)(UIContextMenuConfiguration *))completion; |
| 1134 | @end |
| 1135 | |
| 1136 | #endif |
| 1137 | |
| 1138 | @interface WKContentView (WKInteractionPrivate) |
| 1139 | - (void)accessibilitySpeakSelectionSetContent:(NSString *)string; |
| 1140 | - (NSArray *)webSelectionRectsForSelectionGeometries:(const Vector<WebCore::SelectionGeometry>&)selectionRects; |
| 1141 | - (void)_accessibilityDidGetSelectionRects:(NSArray *)selectionRects withGranularity:(UITextGranularity)granularity atOffset:(NSInteger)offset; |
| 1142 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1143 | - (void)_internalContextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location completion:(void(^)(UIContextMenuConfiguration *))completion; |
| 1144 | #endif |
| 1145 | @end |
| 1146 | |
| 1147 | #define ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG149" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(1147, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0) do { \ |
| 1148 | if (self.shouldUseAsyncInteractions) [[unlikely]] { \ |
| 1149 | RELEASE_LOG_ERROR(TextInteraction, "Received unexpected call to %s", __PRETTY_FUNCTION__)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG150" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; \ |
| 1150 | if (WebKit::isFullWebBrowserOrRunningTest()) \ |
| 1151 | RELEASE_ASSERT_NOT_REACHED()do { WTF::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo(1151, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); \ |
| 1152 | else \ |
| 1153 | ASSERT_NOT_REACHED()((void)0); \ |
| 1154 | } \ |
| 1155 | } while (0) |
| 1156 | |
| 1157 | @implementation WKContentView (WKInteraction) |
| 1158 | |
| 1159 | - (BOOL)preventsPanningInXAxis |
| 1160 | { |
| 1161 | return _preventsPanningInXAxis; |
| 1162 | } |
| 1163 | |
| 1164 | - (BOOL)preventsPanningInYAxis |
| 1165 | { |
| 1166 | return _preventsPanningInYAxis; |
| 1167 | } |
| 1168 | |
| 1169 | - (WKFormInputSession *)_formInputSession |
| 1170 | { |
| 1171 | return _formInputSession.get(); |
| 1172 | } |
| 1173 | |
| 1174 | - (void)_createAndConfigureDoubleTapGestureRecognizer |
| 1175 | { |
| 1176 | if (_doubleTapGestureRecognizer) { |
| 1177 | [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()]; |
| 1178 | [_doubleTapGestureRecognizer setDelegate:nilnullptr]; |
| 1179 | [_doubleTapGestureRecognizer setGestureFailedTarget:nilnullptr action:nilnullptr]; |
| 1180 | } |
| 1181 | |
| 1182 | _doubleTapGestureRecognizer = adoptNS([[WKSyntheticTapGestureRecognizer alloc] initWithTarget:self action:@selector(_doubleTapRecognized:)]); |
| 1183 | [_doubleTapGestureRecognizer setGestureFailedTarget:self action:@selector(_doubleTapDidFail:)]; |
| 1184 | [_doubleTapGestureRecognizer setNumberOfTapsRequired:2]; |
| 1185 | [_doubleTapGestureRecognizer setDelegate:self]; |
| 1186 | [self addGestureRecognizer:_doubleTapGestureRecognizer.get()]; |
| 1187 | [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTapGestureRecognizer.get()]; |
| 1188 | [_keyboardDismissalGestureRecognizer requireGestureRecognizerToFail:_doubleTapGestureRecognizer.get()]; |
| 1189 | } |
| 1190 | |
| 1191 | - (void)_createAndConfigureHighlightLongPressGestureRecognizer |
| 1192 | { |
| 1193 | _highlightLongPressGestureRecognizer = adoptNS([[WKHighlightLongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_highlightLongPressRecognized:)]); |
| 1194 | [_highlightLongPressGestureRecognizer setDelay:highlightDelay]; |
| 1195 | [_highlightLongPressGestureRecognizer setDelegate:self]; |
| 1196 | [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()]; |
| 1197 | } |
| 1198 | |
| 1199 | - (void)_createAndConfigureLongPressGestureRecognizer |
| 1200 | { |
| 1201 | _longPressGestureRecognizer = adoptNS([[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_longPressRecognized:)]); |
| 1202 | [_longPressGestureRecognizer setDelay:tapAndHoldDelay]; |
| 1203 | [_longPressGestureRecognizer setDelegate:self]; |
| 1204 | [_longPressGestureRecognizer _setRequiresQuietImpulse:YES__objc_yes]; |
| 1205 | [self addGestureRecognizer:_longPressGestureRecognizer.get()]; |
| 1206 | } |
| 1207 | |
| 1208 | - (BOOL)shouldUseAsyncInteractions |
| 1209 | { |
| 1210 | return protect(_page->preferences())->useAsyncUIKitInteractions(); |
| 1211 | } |
| 1212 | |
| 1213 | - (BOOL)selectionHonorsOverflowScrolling |
| 1214 | { |
| 1215 | return protect(_page->preferences())->selectionHonorsOverflowScrolling(); |
| 1216 | } |
| 1217 | |
| 1218 | - (ScopeExit<Function<void()>>)makeTextSelectionViewsNonInteractiveForScope |
| 1219 | { |
| 1220 | Vector<RetainPtr<UIView>> viewsToRestore; |
| 1221 | for (UIView *view in [_textInteractionWrapper managedTextSelectionViews]) { |
| 1222 | [view _wk_collectDescendantsIncludingSelf:viewsToRestore matching:^(UIView *view) { |
| 1223 | return view.userInteractionEnabled; |
| 1224 | }]; |
| 1225 | } |
| 1226 | |
| 1227 | for (RetainPtr view : viewsToRestore) |
| 1228 | [view setUserInteractionEnabled:NO__objc_no]; |
| 1229 | |
| 1230 | return makeScopeExit(Function<void()> { [viewsToRestore = WTF::move(viewsToRestore)] { |
| 1231 | for (RetainPtr view : viewsToRestore) |
| 1232 | [view setUserInteractionEnabled:YES__objc_yes]; |
| 1233 | } }); |
| 1234 | } |
| 1235 | |
| 1236 | - (BOOL)_shouldUseUIContextMenuAsyncConfiguration |
| 1237 | { |
| 1238 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 1239 | return self.shouldUseAsyncInteractions; |
| 1240 | #else |
| 1241 | return NO__objc_no; |
| 1242 | #endif |
| 1243 | } |
| 1244 | |
| 1245 | - (BOOL)_shouldUseTextCursorDragAnimator |
| 1246 | { |
| 1247 | #if HAVE(UI_TEXT_CURSOR_DROP_POSITION_ANIMATOR)(defined 1 && 1) |
| 1248 | return YES__objc_yes; |
| 1249 | #else |
| 1250 | return NO__objc_no; |
| 1251 | #endif |
| 1252 | } |
| 1253 | |
| 1254 | - (void)_updateRuntimeProtocolConformanceIfNeeded |
| 1255 | { |
| 1256 | static bool hasUpdatedProtocolConformance = false; |
| 1257 | if (std::exchange(hasUpdatedProtocolConformance, true)) |
| 1258 | return; |
| 1259 | |
| 1260 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 1261 | if (self.shouldUseAsyncInteractions) { |
| 1262 | RELEASE_LOG(TextInteraction, "Conforming to BrowserEngineKit text input protocol")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Conforming to BrowserEngineKit text input protocol"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG151" ) = "Conforming to BrowserEngineKit text input protocol"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Conforming to BrowserEngineKit text input protocol")]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Conforming to BrowserEngineKit text input protocol" ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 1263 | class_addProtocol(self.class, @protocol(BETextInput)); |
| 1264 | } else |
| 1265 | #endif |
| 1266 | { |
| 1267 | RELEASE_LOG(TextInteraction, "Conforming to legacy UIKit interaction and text input protocols")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Conforming to legacy UIKit interaction and text input protocols" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG152" ) = "Conforming to legacy UIKit interaction and text input protocols" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Conforming to legacy UIKit interaction and text input protocols" )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Conforming to legacy UIKit interaction and text input protocols" ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 1268 | auto legacyProtocols = std::array { |
| 1269 | @protocol(UIWKInteractionViewProtocol), |
| 1270 | @protocol(UITextInputPrivate), |
| 1271 | @protocol(UITextAutoscrolling), |
| 1272 | @protocol(UITextInputMultiDocument), |
| 1273 | @protocol(_UITextInputTranslationSupport), |
| 1274 | }; |
| 1275 | for (RetainPtr protocol : legacyProtocols) |
| 1276 | class_addProtocol(self.class, protocol); |
| 1277 | } |
| 1278 | |
| 1279 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1280 | if (!self._shouldUseUIContextMenuAsyncConfiguration) { |
| 1281 | auto legacyAsyncConfigurationSelector = @selector(_contextMenuInteraction:configurationForMenuAtLocation:completion:); |
| 1282 | auto internalAsyncConfigurationSelector = @selector(_internalContextMenuInteraction:configurationForMenuAtLocation:completion:); |
| 1283 | auto internalMethod = class_getInstanceMethod(self.class, internalAsyncConfigurationSelector); |
| 1284 | auto internalImplementation = method_getImplementation(internalMethod); |
| 1285 | class_addMethod(self.class, legacyAsyncConfigurationSelector, internalImplementation, method_getTypeEncoding(internalMethod)); |
| 1286 | } |
| 1287 | #endif // USE(UICONTEXTMENU) |
| 1288 | } |
| 1289 | |
| 1290 | - (void)setUpInteraction |
| 1291 | { |
| 1292 | // If the page is not valid yet then delay interaction setup until the process is launched/relaunched. |
| 1293 | if (!protect(_page)->hasRunningProcess()) |
| 1294 | return; |
| 1295 | |
| 1296 | if (_hasSetUpInteractions) |
| 1297 | return; |
| 1298 | |
| 1299 | _cachedHasCustomTintColor = std::nullopt; |
| 1300 | |
| 1301 | if (!_interactionViewsContainerView) { |
| 1302 | _interactionViewsContainerView = adoptNS([[UIView alloc] init]); |
| 1303 | [_interactionViewsContainerView layer].name = @"InteractionViewsContainer"; |
| 1304 | [_interactionViewsContainerView setOpaque:NO__objc_no]; |
| 1305 | [_interactionViewsContainerView layer].anchorPoint = CGPointZero; |
| 1306 | [self.superview addSubview:_interactionViewsContainerView.get()]; |
| 1307 | } |
| 1308 | |
| 1309 | _keyboardScrollingAnimator = adoptNS([[WKKeyboardScrollViewAnimator alloc] init]); |
| 1310 | [protect(_keyboardScrollingAnimator) setDelegate:self]; |
| 1311 | |
| 1312 | [self.layer addObserver:self forKeyPath:@"transform" options:NSKeyValueObservingOptionInitial context:WKContentViewKVOTransformContext]; |
| 1313 | |
| 1314 | _touchActionLeftSwipeGestureRecognizer = adoptNS([[UISwipeGestureRecognizer alloc] initWithTarget:nilnullptr action:nilnullptr]); |
| 1315 | [_touchActionLeftSwipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionLeft]; |
| 1316 | [_touchActionLeftSwipeGestureRecognizer setDelegate:self]; |
| 1317 | [_touchActionLeftSwipeGestureRecognizer setName:@"Touch action swipe left"]; |
| 1318 | [self addGestureRecognizer:_touchActionLeftSwipeGestureRecognizer.get()]; |
| 1319 | |
| 1320 | _touchActionRightSwipeGestureRecognizer = adoptNS([[UISwipeGestureRecognizer alloc] initWithTarget:nilnullptr action:nilnullptr]); |
| 1321 | [_touchActionRightSwipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionRight]; |
| 1322 | [_touchActionRightSwipeGestureRecognizer setDelegate:self]; |
| 1323 | [_touchActionRightSwipeGestureRecognizer setName:@"Touch action swipe right"]; |
| 1324 | [self addGestureRecognizer:_touchActionRightSwipeGestureRecognizer.get()]; |
| 1325 | |
| 1326 | _touchActionUpSwipeGestureRecognizer = adoptNS([[UISwipeGestureRecognizer alloc] initWithTarget:nilnullptr action:nilnullptr]); |
| 1327 | [_touchActionUpSwipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionUp]; |
| 1328 | [_touchActionUpSwipeGestureRecognizer setDelegate:self]; |
| 1329 | [_touchActionUpSwipeGestureRecognizer setName:@"Touch action swipe up"]; |
| 1330 | [self addGestureRecognizer:_touchActionUpSwipeGestureRecognizer.get()]; |
| 1331 | |
| 1332 | _touchActionDownSwipeGestureRecognizer = adoptNS([[UISwipeGestureRecognizer alloc] initWithTarget:nilnullptr action:nilnullptr]); |
| 1333 | [_touchActionDownSwipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionDown]; |
| 1334 | [_touchActionDownSwipeGestureRecognizer setDelegate:self]; |
| 1335 | [_touchActionDownSwipeGestureRecognizer setName:@"Touch action swipe down"]; |
| 1336 | [self addGestureRecognizer:_touchActionDownSwipeGestureRecognizer.get()]; |
| 1337 | |
| 1338 | _touchStartDeferringGestureRecognizerForImmediatelyResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1339 | [_touchStartDeferringGestureRecognizerForImmediatelyResettableGestures setName:@"Deferrer for touch start (immediate reset)"]; |
| 1340 | |
| 1341 | _touchStartDeferringGestureRecognizerForDelayedResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1342 | [_touchStartDeferringGestureRecognizerForDelayedResettableGestures setName:@"Deferrer for touch start (delayed reset)"]; |
| 1343 | |
| 1344 | _touchStartDeferringGestureRecognizerForSyntheticTapGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1345 | [_touchStartDeferringGestureRecognizerForSyntheticTapGestures setName:@"Deferrer for touch start (synthetic tap)"]; |
| 1346 | |
| 1347 | _touchEndDeferringGestureRecognizerForImmediatelyResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1348 | [_touchEndDeferringGestureRecognizerForImmediatelyResettableGestures setName:@"Deferrer for touch end (immediate reset)"]; |
| 1349 | |
| 1350 | _touchEndDeferringGestureRecognizerForDelayedResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1351 | [_touchEndDeferringGestureRecognizerForDelayedResettableGestures setName:@"Deferrer for touch end (delayed reset)"]; |
| 1352 | |
| 1353 | _touchEndDeferringGestureRecognizerForSyntheticTapGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1354 | [_touchEndDeferringGestureRecognizerForSyntheticTapGestures setName:@"Deferrer for touch end (synthetic tap)"]; |
| 1355 | |
| 1356 | _touchMoveDeferringGestureRecognizer = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1357 | [_touchMoveDeferringGestureRecognizer setName:@"Deferrer for touch move"]; |
| 1358 | |
| 1359 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 1360 | _imageAnalysisDeferringGestureRecognizer = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]); |
| 1361 | [_imageAnalysisDeferringGestureRecognizer setName:@"Deferrer for image analysis"]; |
| 1362 | [_imageAnalysisDeferringGestureRecognizer setImmediatelyFailsAfterTouchEnd:YES__objc_yes]; |
| 1363 | [_imageAnalysisDeferringGestureRecognizer setEnabled:WebKit::isLiveTextAvailableAndEnabled()]; |
| 1364 | #endif |
| 1365 | |
| 1366 | for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) { |
| 1367 | gesture.delegate = self; |
| 1368 | [self addGestureRecognizer:gesture]; |
| 1369 | } |
| 1370 | |
| 1371 | if (_gestureRecognizerConsistencyEnforcer) |
| 1372 | _gestureRecognizerConsistencyEnforcer->reset(); |
| 1373 | |
| 1374 | _touchEventGestureRecognizer = adoptNS([[WKTouchEventsGestureRecognizer alloc] initWithContentView:self]); |
| 1375 | [_touchEventGestureRecognizer setDelegate:self]; |
| 1376 | [self addGestureRecognizer:_touchEventGestureRecognizer.get()]; |
| 1377 | |
| 1378 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 1379 | [self setUpMouseGestureRecognizer]; |
| 1380 | #endif |
| 1381 | |
| 1382 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 1383 | _lookupGestureRecognizer = adoptNS([[_UILookupGestureRecognizer alloc] initWithTarget:self action:@selector(_lookupGestureRecognized:)]); |
| 1384 | [_lookupGestureRecognizer setDelegate:self]; |
| 1385 | [self addGestureRecognizer:_lookupGestureRecognizer.get()]; |
| 1386 | #endif |
| 1387 | |
| 1388 | _singleTapGestureRecognizer = adoptNS([[WKSyntheticTapGestureRecognizer alloc] initWithTarget:self action:@selector(_singleTapRecognized:)]); |
| 1389 | [_singleTapGestureRecognizer setDelegate:self]; |
| 1390 | [_singleTapGestureRecognizer setGestureIdentifiedTarget:self action:@selector(_singleTapIdentified:)]; |
| 1391 | [_singleTapGestureRecognizer setResetTarget:self action:@selector(_singleTapDidReset:)]; |
| 1392 | [_singleTapGestureRecognizer setSupportingTouchEventsGestureRecognizer:_touchEventGestureRecognizer.get()]; |
| 1393 | [self addGestureRecognizer:_singleTapGestureRecognizer.get()]; |
| 1394 | |
| 1395 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 1396 | _modelInteractionPanGestureRecognizer = adoptNS([[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(_modelInteractionPanGestureRecognized:)]); |
| 1397 | [_modelInteractionPanGestureRecognizer setMinimumNumberOfTouches:1]; |
| 1398 | [_modelInteractionPanGestureRecognizer setMaximumNumberOfTouches:1]; |
| 1399 | [_modelInteractionPanGestureRecognizer setDelegate:self]; |
| 1400 | [self addGestureRecognizer:_modelInteractionPanGestureRecognizer.get()]; |
| 1401 | #endif |
| 1402 | |
| 1403 | _nonBlockingDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_nonBlockingDoubleTapRecognized:)]); |
| 1404 | [_nonBlockingDoubleTapGestureRecognizer setNumberOfTapsRequired:2]; |
| 1405 | [_nonBlockingDoubleTapGestureRecognizer setDelegate:self]; |
| 1406 | [_nonBlockingDoubleTapGestureRecognizer setEnabled:NO__objc_no]; |
| 1407 | [self addGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()]; |
| 1408 | |
| 1409 | _doubleTapGestureRecognizerForDoubleClick = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_doubleTapRecognizedForDoubleClick:)]); |
| 1410 | [_doubleTapGestureRecognizerForDoubleClick setNumberOfTapsRequired:2]; |
| 1411 | [_doubleTapGestureRecognizerForDoubleClick setDelegate:self]; |
| 1412 | [self addGestureRecognizer:_doubleTapGestureRecognizerForDoubleClick.get()]; |
| 1413 | |
| 1414 | [self _createAndConfigureDoubleTapGestureRecognizer]; |
| 1415 | |
| 1416 | _twoFingerDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_twoFingerDoubleTapRecognized:)]); |
| 1417 | [_twoFingerDoubleTapGestureRecognizer setNumberOfTapsRequired:2]; |
| 1418 | [_twoFingerDoubleTapGestureRecognizer setNumberOfTouchesRequired:2]; |
| 1419 | [_twoFingerDoubleTapGestureRecognizer setDelegate:self]; |
| 1420 | [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()]; |
| 1421 | |
| 1422 | [self _createAndConfigureHighlightLongPressGestureRecognizer]; |
| 1423 | [self _createAndConfigureLongPressGestureRecognizer]; |
| 1424 | |
| 1425 | [self _updateLongPressAndHighlightLongPressGestures]; |
| 1426 | |
| 1427 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 1428 | [self setUpDragAndDropInteractions]; |
| 1429 | #endif |
| 1430 | |
| 1431 | #if HAVE(UI_POINTER_INTERACTION)(defined 1 && 1) |
| 1432 | [self setUpPointerInteraction]; |
| 1433 | #endif |
| 1434 | |
| 1435 | #if HAVE(PENCILKIT_TEXT_INPUT)(defined 1 && 1) |
| 1436 | [self setUpScribbleInteraction]; |
| 1437 | #endif |
| 1438 | |
| 1439 | _twoFingerSingleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_twoFingerSingleTapGestureRecognized:)]); |
| 1440 | [_twoFingerSingleTapGestureRecognizer setAllowableMovement:60]; |
| 1441 | [_twoFingerSingleTapGestureRecognizer _setAllowableSeparation:150]; |
| 1442 | [_twoFingerSingleTapGestureRecognizer setNumberOfTapsRequired:1]; |
| 1443 | [_twoFingerSingleTapGestureRecognizer setNumberOfTouchesRequired:2]; |
| 1444 | [_twoFingerSingleTapGestureRecognizer setDelaysTouchesEnded:NO__objc_no]; |
| 1445 | [_twoFingerSingleTapGestureRecognizer setDelegate:self]; |
| 1446 | [_twoFingerSingleTapGestureRecognizer setEnabled:!self.webView._editable]; |
| 1447 | [self addGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()]; |
| 1448 | |
| 1449 | _touchActionGestureRecognizer = adoptNS([[WKTouchActionGestureRecognizer alloc] initWithTouchActionDelegate:self]); |
| 1450 | [self addGestureRecognizer:_touchActionGestureRecognizer.get()]; |
| 1451 | |
| 1452 | // FIXME: This should be called when we get notified that loading has completed. |
| 1453 | [self setUpTextSelectionAssistant]; |
| 1454 | |
| 1455 | _keyboardDismissalGestureRecognizer = adoptNS([[WKScrollViewTrackingTapGestureRecognizer alloc] initWithTarget:self action:@selector(_keyboardDismissalGestureRecognized:)]); |
| 1456 | [_keyboardDismissalGestureRecognizer setNumberOfTapsRequired:1]; |
| 1457 | [_keyboardDismissalGestureRecognizer setDelegate:self]; |
| 1458 | [_keyboardDismissalGestureRecognizer setName:@"Keyboard dismissal tap gesture"]; |
| 1459 | [_keyboardDismissalGestureRecognizer setEnabled:protect(_page->preferences())->keyboardDismissalGestureEnabled()]; |
| 1460 | [self addGestureRecognizer:_keyboardDismissalGestureRecognizer.get()]; |
| 1461 | |
| 1462 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 1463 | _gamepadInteractionGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_gamepadInteractionGestureRecognized:)]); |
| 1464 | [_gamepadInteractionGestureRecognizer setDelegate:self]; |
| 1465 | [_gamepadInteractionGestureRecognizer setName:@"Gamepad interaction menu gesture"]; |
| 1466 | _gamepadInteractionGestureRecognizer.get().allowedTouchTypes = @[]; |
| 1467 | _gamepadInteractionGestureRecognizer.get().allowedPressTypes = @[@(UIPressTypeMenu)]; |
| 1468 | [self addGestureRecognizer:_gamepadInteractionGestureRecognizer.get()]; |
| 1469 | #endif |
| 1470 | |
| 1471 | #if HAVE(UI_PASTE_CONFIGURATION)(defined 1 && 1) |
| 1472 | self.pasteConfiguration = adoptNS([[UIPasteConfiguration alloc] initWithAcceptableTypeIdentifiers:protect([&] { |
| 1473 | if (protect(_page->preferences())->attachmentElementEnabled()) |
| 1474 | return WebKit::supportedRichTextPasteboardTypesWithAttachmentsForPasteConfiguration(); |
| 1475 | return WebKit::supportedRichTextPasteboardTypesForPasteConfiguration(); |
| 1476 | }())]); |
| 1477 | #endif |
| 1478 | |
| 1479 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) |
| 1480 | [self _registerPreview]; |
| 1481 | #endif |
| 1482 | |
| 1483 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; |
| 1484 | |
| 1485 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 1486 | [center addObserver:self selector:@selector(_willHideMenu:) name:UIMenuControllerWillHideMenuNotification object:nilnullptr]; |
| 1487 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 1488 | |
| 1489 | [center addObserver:self selector:@selector(_keyboardDidRequestDismissal:) name:UIKeyboardPrivateDidRequestDismissalNotification object:nilnullptr]; |
| 1490 | |
| 1491 | _actionSheetAssistant = adoptNS([[WKActionSheetAssistant alloc] initWithView:self]); |
| 1492 | [protect(_actionSheetAssistant) setDelegate:self]; |
| 1493 | _smartMagnificationController = WebKit::SmartMagnificationController::create(self); |
| 1494 | _touchEventsCanPreventNativeGestures = YES__objc_yes; |
| 1495 | _isExpectingFastSingleTapCommit = NO__objc_no; |
| 1496 | _potentialTapInProgress = NO__objc_no; |
| 1497 | _isDoubleTapPending = NO__objc_no; |
| 1498 | _showDebugTapHighlightsForFastClicking = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitShowFastClickDebugTapHighlights"]; |
| 1499 | _needsDeferredEndScrollingSelectionUpdate = NO__objc_no; |
| 1500 | _isChangingFocus = NO__objc_no; |
| 1501 | _isBlurringFocusedElement = NO__objc_no; |
| 1502 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1503 | _isDisplayingContextMenuWithAnimation = NO__objc_no; |
| 1504 | _isPreparingToDisplayContextMenu = NO__objc_no; |
| 1505 | #endif |
| 1506 | _isUpdatingAccessoryView = NO__objc_no; |
| 1507 | |
| 1508 | _dataListTextSuggestionsInputView = nilnullptr; |
| 1509 | _dataListTextSuggestions = nilnullptr; |
| 1510 | |
| 1511 | #if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)(defined 0 && 0) |
| 1512 | _textCheckingController = makeUnique<WebKit::TextCheckingController>(*_page); |
| 1513 | #endif |
| 1514 | |
| 1515 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 1516 | |
| 1517 | protect(_page->legacyMainFrameProcess())->updateTextCheckerState(); |
| 1518 | protect(_page)->setScreenIsBeingCaptured([self screenIsBeingCaptured]); |
| 1519 | |
| 1520 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 1521 | [self _setUpImageAnalysis]; |
| 1522 | #endif |
| 1523 | |
| 1524 | _sourceAnimationIDtoDestinationAnimationID = adoptNS([[NSMutableDictionary alloc] init]); |
| 1525 | |
| 1526 | _selectionInteractionType = SelectionInteractionType::None; |
| 1527 | |
| 1528 | _editingEndedByUser = YES__objc_yes; |
| 1529 | |
| 1530 | _hasSetUpInteractions = YES__objc_yes; |
| 1531 | } |
| 1532 | |
| 1533 | - (void)cleanUpInteraction |
| 1534 | { |
| 1535 | if (!_hasSetUpInteractions) |
| 1536 | return; |
| 1537 | |
| 1538 | _textInteractionWrapper = nilnullptr; |
| 1539 | |
| 1540 | [protect(_actionSheetAssistant) cleanupSheet]; |
| 1541 | _actionSheetAssistant = nilnullptr; |
| 1542 | |
| 1543 | _smartMagnificationController = nilnullptr; |
| 1544 | _didAccessoryTabInitiateFocus = NO__objc_no; |
| 1545 | _isChangingFocusUsingAccessoryTab = NO__objc_no; |
| 1546 | _isExpectingFastSingleTapCommit = NO__objc_no; |
| 1547 | _needsDeferredEndScrollingSelectionUpdate = NO__objc_no; |
| 1548 | [_formInputSession invalidate]; |
| 1549 | _formInputSession = nilnullptr; |
| 1550 | [_tapHighlightView removeFromSuperview]; |
| 1551 | _lastOutstandingPositionInformationRequest = std::nullopt; |
| 1552 | _isWaitingOnPositionInformation = NO__objc_no; |
| 1553 | |
| 1554 | _focusRequiresStrongPasswordAssistance = NO__objc_no; |
| 1555 | _additionalContextForStrongPasswordAssistance = nilnullptr; |
| 1556 | _waitingForEditDragSnapshot = NO__objc_no; |
| 1557 | |
| 1558 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 1559 | _lastAutocorrectionContext = { }; |
| 1560 | |
| 1561 | _candidateViewNeedsUpdate = NO__objc_no; |
| 1562 | _seenHardwareKeyDownInNonEditableElement = NO__objc_no; |
| 1563 | |
| 1564 | _textInteractionDidChangeFocusedElement = NO__objc_no; |
| 1565 | _activeTextInteractionCount = 0; |
| 1566 | |
| 1567 | _treatAsContentEditableUntilNextEditorStateUpdate = NO__objc_no; |
| 1568 | _isHandlingActiveKeyEvent = NO__objc_no; |
| 1569 | _isHandlingActivePressesEvent = NO__objc_no; |
| 1570 | _isDeferringKeyEventsToInputMethod = NO__objc_no; |
| 1571 | _usingMouseDragForSelection = NO__objc_no; |
| 1572 | |
| 1573 | if (_interactionViewsContainerView) { |
| 1574 | [self.layer removeObserver:self forKeyPath:@"transform" context:WKContentViewKVOTransformContext]; |
| 1575 | [_interactionViewsContainerView removeFromSuperview]; |
| 1576 | _interactionViewsContainerView = nilnullptr; |
| 1577 | } |
| 1578 | |
| 1579 | _waitingForKeyboardAppearanceAnimationToStart = NO__objc_no; |
| 1580 | _lastInsertedCharacterToOverrideCharacterBeforeSelection = std::nullopt; |
| 1581 | |
| 1582 | [_touchEventGestureRecognizer setDelegate:nilnullptr]; |
| 1583 | [self removeGestureRecognizer:_touchEventGestureRecognizer.get()]; |
| 1584 | |
| 1585 | for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) { |
| 1586 | gesture.delegate = nilnullptr; |
| 1587 | [self removeGestureRecognizer:gesture]; |
| 1588 | } |
| 1589 | |
| 1590 | if (_gestureRecognizerConsistencyEnforcer) |
| 1591 | _gestureRecognizerConsistencyEnforcer->reset(); |
| 1592 | |
| 1593 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 1594 | [self removeInteraction:_mouseInteraction.get()]; |
| 1595 | #endif |
| 1596 | |
| 1597 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 1598 | [_lookupGestureRecognizer setDelegate:nilnullptr]; |
| 1599 | [self removeGestureRecognizer:_lookupGestureRecognizer.get()]; |
| 1600 | #endif |
| 1601 | |
| 1602 | [_keyboardDismissalGestureRecognizer setDelegate:nilnullptr]; |
| 1603 | [self removeGestureRecognizer:_keyboardDismissalGestureRecognizer.get()]; |
| 1604 | |
| 1605 | [_singleTapGestureRecognizer setDelegate:nilnullptr]; |
| 1606 | [_singleTapGestureRecognizer setGestureIdentifiedTarget:nilnullptr action:nilnullptr]; |
| 1607 | [_singleTapGestureRecognizer setResetTarget:nilnullptr action:nilnullptr]; |
| 1608 | [_singleTapGestureRecognizer setSupportingTouchEventsGestureRecognizer:nilnullptr]; |
| 1609 | [self removeGestureRecognizer:_singleTapGestureRecognizer.get()]; |
| 1610 | |
| 1611 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 1612 | [_modelInteractionPanGestureRecognizer setDelegate:nilnullptr]; |
| 1613 | [self removeGestureRecognizer:_modelInteractionPanGestureRecognizer.get()]; |
| 1614 | #endif |
| 1615 | |
| 1616 | [_highlightLongPressGestureRecognizer setDelegate:nilnullptr]; |
| 1617 | [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()]; |
| 1618 | |
| 1619 | [_longPressGestureRecognizer setDelegate:nilnullptr]; |
| 1620 | [self removeGestureRecognizer:_longPressGestureRecognizer.get()]; |
| 1621 | |
| 1622 | [_doubleTapGestureRecognizer setDelegate:nilnullptr]; |
| 1623 | [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()]; |
| 1624 | |
| 1625 | [_nonBlockingDoubleTapGestureRecognizer setDelegate:nilnullptr]; |
| 1626 | [self removeGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()]; |
| 1627 | |
| 1628 | [_doubleTapGestureRecognizerForDoubleClick setDelegate:nilnullptr]; |
| 1629 | [self removeGestureRecognizer:_doubleTapGestureRecognizerForDoubleClick.get()]; |
| 1630 | |
| 1631 | [_twoFingerDoubleTapGestureRecognizer setDelegate:nilnullptr]; |
| 1632 | [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()]; |
| 1633 | |
| 1634 | [_twoFingerSingleTapGestureRecognizer setDelegate:nilnullptr]; |
| 1635 | [self removeGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()]; |
| 1636 | |
| 1637 | [self removeGestureRecognizer:_touchActionGestureRecognizer.get()]; |
| 1638 | [self removeGestureRecognizer:_touchActionLeftSwipeGestureRecognizer.get()]; |
| 1639 | [self removeGestureRecognizer:_touchActionRightSwipeGestureRecognizer.get()]; |
| 1640 | [self removeGestureRecognizer:_touchActionUpSwipeGestureRecognizer.get()]; |
| 1641 | [self removeGestureRecognizer:_touchActionDownSwipeGestureRecognizer.get()]; |
| 1642 | |
| 1643 | _layerTreeTransactionIdAtLastInteractionStart = { }; |
| 1644 | |
| 1645 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 1646 | [protect(existingLocalDragSessionContext(protect(_dragDropInteractionState.dragSession()))) cleanUpTemporaryDirectories]; |
| 1647 | [self teardownDragAndDropInteractions]; |
| 1648 | #endif |
| 1649 | |
| 1650 | #if HAVE(UI_POINTER_INTERACTION)(defined 1 && 1) |
| 1651 | [self removeInteraction:_pointerInteraction.get()]; |
| 1652 | _pointerInteraction = nilnullptr; |
| 1653 | _pointerInteractionRegionNeedsUpdate = NO__objc_no; |
| 1654 | #endif |
| 1655 | |
| 1656 | _revealFocusedElementDeferrer = nullptr; |
| 1657 | |
| 1658 | #if HAVE(PENCILKIT_TEXT_INPUT)(defined 1 && 1) |
| 1659 | [self cleanUpScribbleInteraction]; |
| 1660 | #endif |
| 1661 | |
| 1662 | _inspectorNodeSearchEnabled = NO__objc_no; |
| 1663 | if (_inspectorNodeSearchGestureRecognizer) { |
| 1664 | [_inspectorNodeSearchGestureRecognizer setDelegate:nilnullptr]; |
| 1665 | [self removeGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()]; |
| 1666 | _inspectorNodeSearchGestureRecognizer = nilnullptr; |
| 1667 | } |
| 1668 | |
| 1669 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) |
| 1670 | [self _unregisterPreview]; |
| 1671 | #endif |
| 1672 | |
| 1673 | [self dismissPickersIfNeededWithReason:WebKit::PickerDismissalReason::ProcessExited]; |
| 1674 | |
| 1675 | [self stopDeferringInputViewUpdatesForAllSources]; |
| 1676 | _focusedElementInformation = { }; |
| 1677 | |
| 1678 | [protect(_keyboardScrollingAnimator) invalidate]; |
| 1679 | _keyboardScrollingAnimator = nilnullptr; |
| 1680 | |
| 1681 | _dataListTextSuggestionsInputView = nilnullptr; |
| 1682 | _dataListTextSuggestions = nilnullptr; |
| 1683 | |
| 1684 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 1685 | [self _tearDownImageAnalysis]; |
| 1686 | #endif |
| 1687 | |
| 1688 | [_webView.get() _updateFixedContainerEdges:WebCore::FixedContainerEdges { }]; |
| 1689 | |
| 1690 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1691 | [self _removeContextMenuHintContainerIfPossible]; |
| 1692 | #endif |
| 1693 | [self _removeContainerForDragPreviews]; |
| 1694 | [self _removeContainerForDropPreviews]; |
| 1695 | [self unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded]; |
| 1696 | |
| 1697 | _hasSetUpInteractions = NO__objc_no; |
| 1698 | _suppressSelectionAssistantReasons = { }; |
| 1699 | |
| 1700 | [self _resetPanningPreventionFlags]; |
| 1701 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 1702 | [self _cancelPendingKeyEventHandlers:NO__objc_no]; |
| 1703 | |
| 1704 | #if HAVE(UI_PASTE_CONFIGURATION)(defined 1 && 1) |
| 1705 | self.pasteConfiguration = nilnullptr; |
| 1706 | #endif |
| 1707 | |
| 1708 | _selectionInteractionType = SelectionInteractionType::None; |
| 1709 | _lastSelectionChildScrollViewContentOffset = std::nullopt; |
| 1710 | _lastSelectionContainerViewOrigin = std::nullopt; |
| 1711 | _lastSiblingBeforeSelectionHighlight = nilnullptr; |
| 1712 | _waitingForEditorStateAfterScrollingSelectionContainer = NO__objc_no; |
| 1713 | |
| 1714 | _cachedHasCustomTintColor = std::nullopt; |
| 1715 | _cachedSelectedTextRange = nilnullptr; |
| 1716 | _cachedSelectionContainerView = nilnullptr; |
| 1717 | } |
| 1718 | |
| 1719 | - (void)cleanUpInteractionPreviewContainers |
| 1720 | { |
| 1721 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 1722 | [self _removeContextMenuHintContainerIfPossible]; |
| 1723 | #endif |
| 1724 | } |
| 1725 | |
| 1726 | - (void)_cancelPendingKeyEventHandlers:(BOOL)handled |
| 1727 | { |
| 1728 | for (auto [event, keyEventHandler] : std::exchange(_keyWebEventHandlers, { })) |
| 1729 | keyEventHandler(event.get(), handled); |
| 1730 | } |
| 1731 | |
| 1732 | - (void)_removeDefaultGestureRecognizers |
| 1733 | { |
| 1734 | for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) |
| 1735 | [self removeGestureRecognizer:gesture]; |
| 1736 | [self removeGestureRecognizer:_touchEventGestureRecognizer.get()]; |
| 1737 | [self removeGestureRecognizer:_singleTapGestureRecognizer.get()]; |
| 1738 | [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()]; |
| 1739 | [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()]; |
| 1740 | [self removeGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()]; |
| 1741 | [self removeGestureRecognizer:_doubleTapGestureRecognizerForDoubleClick.get()]; |
| 1742 | [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()]; |
| 1743 | [self removeGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()]; |
| 1744 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 1745 | [self removeGestureRecognizer:_modelInteractionPanGestureRecognizer.get()]; |
| 1746 | #endif |
| 1747 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 1748 | [self removeInteraction:_mouseInteraction.get()]; |
| 1749 | #endif |
| 1750 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 1751 | [self removeGestureRecognizer:_lookupGestureRecognizer.get()]; |
| 1752 | #endif |
| 1753 | [self removeGestureRecognizer:_touchActionGestureRecognizer.get()]; |
| 1754 | [self removeGestureRecognizer:_touchActionLeftSwipeGestureRecognizer.get()]; |
| 1755 | [self removeGestureRecognizer:_touchActionRightSwipeGestureRecognizer.get()]; |
| 1756 | [self removeGestureRecognizer:_touchActionUpSwipeGestureRecognizer.get()]; |
| 1757 | [self removeGestureRecognizer:_touchActionDownSwipeGestureRecognizer.get()]; |
| 1758 | [self removeGestureRecognizer:_keyboardDismissalGestureRecognizer.get()]; |
| 1759 | } |
| 1760 | |
| 1761 | - (void)_addDefaultGestureRecognizers |
| 1762 | { |
| 1763 | for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) |
| 1764 | [self addGestureRecognizer:gesture]; |
| 1765 | [self addGestureRecognizer:_touchEventGestureRecognizer.get()]; |
| 1766 | [self addGestureRecognizer:_singleTapGestureRecognizer.get()]; |
| 1767 | [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()]; |
| 1768 | [self addGestureRecognizer:_doubleTapGestureRecognizer.get()]; |
| 1769 | [self addGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()]; |
| 1770 | [self addGestureRecognizer:_doubleTapGestureRecognizerForDoubleClick.get()]; |
| 1771 | [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()]; |
| 1772 | [self addGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()]; |
| 1773 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 1774 | [self addGestureRecognizer:_modelInteractionPanGestureRecognizer.get()]; |
| 1775 | #endif |
| 1776 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 1777 | [self addInteraction:_mouseInteraction.get()]; |
| 1778 | #endif |
| 1779 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 1780 | [self addGestureRecognizer:_lookupGestureRecognizer.get()]; |
| 1781 | #endif |
| 1782 | [self addGestureRecognizer:_touchActionGestureRecognizer.get()]; |
| 1783 | [self addGestureRecognizer:_touchActionLeftSwipeGestureRecognizer.get()]; |
| 1784 | [self addGestureRecognizer:_touchActionRightSwipeGestureRecognizer.get()]; |
| 1785 | [self addGestureRecognizer:_touchActionUpSwipeGestureRecognizer.get()]; |
| 1786 | [self addGestureRecognizer:_touchActionDownSwipeGestureRecognizer.get()]; |
| 1787 | [self addGestureRecognizer:_keyboardDismissalGestureRecognizer.get()]; |
| 1788 | |
| 1789 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 1790 | [self addGestureRecognizer:_gamepadInteractionGestureRecognizer.get()]; |
| 1791 | #endif |
| 1792 | } |
| 1793 | |
| 1794 | - (void)_didChangeLinkPreviewAvailability |
| 1795 | { |
| 1796 | [self _updateLongPressAndHighlightLongPressGestures]; |
| 1797 | } |
| 1798 | |
| 1799 | - (void)_updateLongPressAndHighlightLongPressGestures |
| 1800 | { |
| 1801 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) |
| 1802 | // We only disable the highlight long press gesture in the case where UIContextMenu is available and we |
| 1803 | // also allow link previews, since the context menu interaction's gestures need to take precedence over |
| 1804 | // highlight long press gestures. |
| 1805 | [_highlightLongPressGestureRecognizer setEnabled:!self._shouldUseContextMenus || !self.webView.allowsLinkPreview]; |
| 1806 | |
| 1807 | // We only enable the long press gesture in the case where the app is linked on iOS 12 or earlier (and |
| 1808 | // therefore prefers the legacy action sheet over context menus), and link previews are also enabled. |
| 1809 | [_longPressGestureRecognizer setEnabled:!self._shouldUseContextMenus && self.webView.allowsLinkPreview]; |
| 1810 | #else |
| 1811 | [_highlightLongPressGestureRecognizer setEnabled:NO__objc_no]; |
| 1812 | [_longPressGestureRecognizer setEnabled:NO__objc_no]; |
| 1813 | #endif |
| 1814 | } |
| 1815 | |
| 1816 | - (UIView *)unscaledView |
| 1817 | { |
| 1818 | return _interactionViewsContainerView.get(); |
| 1819 | } |
| 1820 | |
| 1821 | - (UIScrollView *)_scroller |
| 1822 | { |
| 1823 | return [_webView.get() scrollView]; |
| 1824 | } |
| 1825 | |
| 1826 | - (CGRect)unobscuredContentRect |
| 1827 | { |
| 1828 | return protect(_page)->unobscuredContentRect(); |
| 1829 | } |
| 1830 | |
| 1831 | #pragma mark - UITextAutoscrolling |
| 1832 | |
| 1833 | - (void)startAutoscroll:(CGPoint)pointInDocument |
| 1834 | { |
| 1835 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG153" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(1835, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 1836 | |
| 1837 | protect(_page)->startAutoscrollAtPosition(pointInDocument); |
| 1838 | } |
| 1839 | |
| 1840 | - (void)cancelAutoscroll |
| 1841 | { |
| 1842 | _selectionInteractionType = SelectionInteractionType::None; |
| 1843 | protect(_page)->cancelAutoscroll(); |
| 1844 | } |
| 1845 | |
| 1846 | - (void)scrollSelectionToVisible:(BOOL)animated |
| 1847 | { |
| 1848 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG154" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(1848, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 1849 | // Used to scroll selection on keyboard up; we already scroll to visible. |
| 1850 | } |
| 1851 | |
| 1852 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context |
| 1853 | { |
| 1854 | if (context != WKContentViewKVOTransformContext) { |
| 1855 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; |
| 1856 | return; |
| 1857 | } |
| 1858 | |
| 1859 | ASSERT([keyPath isEqualToString:@"transform"])((void)0); |
| 1860 | ASSERT(object == self.layer)((void)0); |
| 1861 | |
| 1862 | if ([UIView _isInAnimationBlock] && protect(_page)->editorState().selectionType == WebCore::SelectionType::None) { |
| 1863 | // If the utility views are not already visible, we don't want them to become visible during the animation since |
| 1864 | // they could not start from a reasonable state. |
| 1865 | // This is not perfect since views could also get updated during the animation, in practice this is rare and the end state |
| 1866 | // remains correct. |
| 1867 | [self _cancelInteraction]; |
| 1868 | [_interactionViewsContainerView setHidden:YES__objc_yes]; |
| 1869 | [UIView _addCompletion:^(BOOL){ [_interactionViewsContainerView setHidden:NO__objc_no]; }]; |
| 1870 | } |
| 1871 | |
| 1872 | [self _updateTapHighlight]; |
| 1873 | |
| 1874 | if (protect(_page)->editorState().selectionType == WebCore::SelectionType::None && _lastSelectionDrawingInfo.type == WebCore::SelectionType::None) |
| 1875 | return; |
| 1876 | |
| 1877 | _selectionNeedsUpdate = YES__objc_yes; |
| 1878 | [self _updateChangedSelection:YES__objc_yes]; |
| 1879 | } |
| 1880 | |
| 1881 | - (void)_enableInspectorNodeSearch |
| 1882 | { |
| 1883 | _inspectorNodeSearchEnabled = YES__objc_yes; |
| 1884 | |
| 1885 | [self _cancelInteraction]; |
| 1886 | |
| 1887 | [self _removeDefaultGestureRecognizers]; |
| 1888 | _inspectorNodeSearchGestureRecognizer = adoptNS([[WKInspectorNodeSearchGestureRecognizer alloc] initWithTarget:self action:@selector(_inspectorNodeSearchRecognized:)]); |
| 1889 | [self addGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()]; |
| 1890 | } |
| 1891 | |
| 1892 | - (void)_disableInspectorNodeSearch |
| 1893 | { |
| 1894 | _inspectorNodeSearchEnabled = NO__objc_no; |
| 1895 | |
| 1896 | [self _addDefaultGestureRecognizers]; |
| 1897 | [self removeGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()]; |
| 1898 | _inspectorNodeSearchGestureRecognizer = nilnullptr; |
| 1899 | } |
| 1900 | |
| 1901 | - (UIView *)hitTest:(CGPoint)point withEvent:(::UIEvent *)event |
| 1902 | { |
| 1903 | for (UIView *subView in [_interactionViewsContainerView.get() subviews]) { |
| 1904 | UIView *hitView = [subView hitTest:[subView convertPoint:point fromView:self] withEvent:event]; |
| 1905 | if (hitView) { |
| 1906 | LOG_WITH_STREAM(UIHitTesting, stream << self << "hitTest at " << WebCore::FloatPoint(point) << " found interaction view " << hitView)((void)0); |
| 1907 | return hitView; |
| 1908 | } |
| 1909 | } |
| 1910 | |
| 1911 | LOG_WITH_STREAM(UIHitTesting, stream << "hit-testing WKContentView subviews " << [[self recursiveDescription] UTF8String])((void)0); |
| 1912 | UIView* hitView = [super hitTest:point withEvent:event]; |
| 1913 | LOG_WITH_STREAM(UIHitTesting, stream << " found view " << [hitView class] << " " << (void*)hitView)((void)0); |
| 1914 | return hitView; |
| 1915 | } |
| 1916 | |
| 1917 | - (const WebKit::InteractionInformationAtPosition&)positionInformation |
| 1918 | { |
| 1919 | return _positionInformation; |
| 1920 | } |
| 1921 | |
| 1922 | - (void)setInputDelegate:(id <UITextInputDelegate>)inputDelegate |
| 1923 | { |
| 1924 | _inputDelegate = inputDelegate; |
| 1925 | } |
| 1926 | |
| 1927 | - (id <UITextInputDelegate>)inputDelegate |
| 1928 | { |
| 1929 | return _inputDelegate.getAutoreleased(); |
| 1930 | } |
| 1931 | |
| 1932 | - (CGPoint)lastInteractionLocation |
| 1933 | { |
| 1934 | return _lastInteractionLocation; |
| 1935 | } |
| 1936 | |
| 1937 | - (BOOL)shouldHideSelectionInFixedPositionWhenScrolling |
| 1938 | { |
| 1939 | if (self.selectionHonorsOverflowScrolling) |
| 1940 | return NO__objc_no; |
| 1941 | |
| 1942 | if (_isEditable) |
| 1943 | return _focusedElementInformation.insideFixedPosition; |
| 1944 | |
| 1945 | auto& editorState = protect(_page)->editorState(); |
| 1946 | return editorState.hasPostLayoutData() && editorState.selectionType == WebCore::SelectionType::Range && editorState.postLayoutData->insideFixedPosition; |
| 1947 | } |
| 1948 | |
| 1949 | - (BOOL)isEditable |
| 1950 | { |
| 1951 | return _isEditable; |
| 1952 | } |
| 1953 | |
| 1954 | - (BOOL)setIsEditable:(BOOL)isEditable |
| 1955 | { |
| 1956 | if (isEditable == _isEditable) |
| 1957 | return NO__objc_no; |
| 1958 | |
| 1959 | _isEditable = isEditable; |
| 1960 | return YES__objc_yes; |
| 1961 | } |
| 1962 | |
| 1963 | - (void)_endEditing |
| 1964 | { |
| 1965 | [_inputPeripheral endEditing]; |
| 1966 | [_formInputSession endEditing]; |
| 1967 | [_dataListTextSuggestionsInputView controlEndEditing]; |
| 1968 | } |
| 1969 | |
| 1970 | - (void)_cancelPreviousResetInputViewDeferralRequest |
| 1971 | { |
| 1972 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stopDeferringInputViewUpdatesForAllSources) object:nilnullptr]; |
| 1973 | } |
| 1974 | |
| 1975 | - (void)_scheduleResetInputViewDeferralAfterBecomingFirstResponder |
| 1976 | { |
| 1977 | [self _cancelPreviousResetInputViewDeferralRequest]; |
| 1978 | |
| 1979 | const NSTimeInterval inputViewDeferralWatchdogTimerDuration = 0.5; |
| 1980 | [self performSelector:@selector(stopDeferringInputViewUpdatesForAllSources) withObject:self afterDelay:inputViewDeferralWatchdogTimerDuration]; |
| 1981 | } |
| 1982 | |
| 1983 | - (BOOL)canBecomeFirstResponder |
| 1984 | { |
| 1985 | return _becomingFirstResponder; |
| 1986 | } |
| 1987 | |
| 1988 | - (BOOL)canBecomeFirstResponderForWebView |
| 1989 | { |
| 1990 | if (_resigningFirstResponder) |
| 1991 | return NO__objc_no; |
| 1992 | // We might want to return something else |
| 1993 | // if we decide to enable/disable interaction programmatically. |
| 1994 | return YES__objc_yes; |
| 1995 | } |
| 1996 | |
| 1997 | - (BOOL)becomeFirstResponder |
| 1998 | { |
| 1999 | return [_webView.get() becomeFirstResponder]; |
| 2000 | } |
| 2001 | |
| 2002 | - (BOOL)becomeFirstResponderForWebView |
| 2003 | { |
| 2004 | if (_resigningFirstResponder) |
| 2005 | return NO__objc_no; |
| 2006 | |
| 2007 | [self startDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::BecomeFirstResponder]; |
| 2008 | |
| 2009 | BOOL didBecomeFirstResponder; |
| 2010 | { |
| 2011 | SetForScope becomingFirstResponder { _becomingFirstResponder, YES__objc_yes }; |
| 2012 | didBecomeFirstResponder = [super becomeFirstResponder]; |
| 2013 | } |
| 2014 | |
| 2015 | if (didBecomeFirstResponder) { |
| 2016 | protect(_page)->installActivityStateChangeCompletionHandler([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 2017 | if (!weakSelf) |
| 2018 | return; |
| 2019 | |
| 2020 | auto strongSelf = weakSelf.get(); |
| 2021 | [strongSelf stopDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::BecomeFirstResponder]; |
| 2022 | }); |
| 2023 | |
| 2024 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 2025 | WebKit::UIGamepadProvider::singleton().viewBecameActive(*protect(_page)); |
| 2026 | #endif |
| 2027 | |
| 2028 | protect(_page)->activityStateDidChange(WebCore::ActivityState::IsFocused, WebKit::WebPageProxy::ActivityStateChangeDispatchMode::Immediate); |
| 2029 | |
| 2030 | if (self._shouldActivateSelectionAfterBecomingFirstResponder) { |
| 2031 | protect(_page)->callAfterNextPresentationUpdate([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 2032 | RetainPtr strongSelf = weakSelf.get(); |
| 2033 | if (![strongSelf _shouldActivateSelectionAfterBecomingFirstResponder]) |
| 2034 | return; |
| 2035 | |
| 2036 | [strongSelf->_textInteractionWrapper setNeedsSelectionUpdate]; |
| 2037 | [strongSelf->_textInteractionWrapper activateSelection]; |
| 2038 | }); |
| 2039 | } |
| 2040 | |
| 2041 | [self _scheduleResetInputViewDeferralAfterBecomingFirstResponder]; |
| 2042 | } else |
| 2043 | [self stopDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::BecomeFirstResponder]; |
| 2044 | |
| 2045 | return didBecomeFirstResponder; |
| 2046 | } |
| 2047 | |
| 2048 | - (BOOL)_shouldActivateSelectionAfterBecomingFirstResponder |
| 2049 | { |
| 2050 | return self.canShowNonEmptySelectionView || (!_suppressSelectionAssistantReasons && _activeTextInteractionCount); |
| 2051 | } |
| 2052 | |
| 2053 | - (BOOL)resignFirstResponder |
| 2054 | { |
| 2055 | return [_webView.get() resignFirstResponder]; |
| 2056 | } |
| 2057 | |
| 2058 | typedef NS_ENUM(NSInteger, EndEditingReason)enum __attribute__((enum_extensibility(open))) EndEditingReason : NSInteger EndEditingReason; enum EndEditingReason : NSInteger { |
| 2059 | EndEditingReasonAccessoryDone, |
| 2060 | EndEditingReasonResigningFirstResponder, |
| 2061 | }; |
| 2062 | |
| 2063 | - (void)endEditingAndUpdateFocusAppearanceWithReason:(EndEditingReason)reason |
| 2064 | { |
| 2065 | _editingEndedByUser = YES__objc_yes; |
| 2066 | |
| 2067 | if (!self.webView._retainingActiveFocusedState) { |
| 2068 | // We need to complete the editing operation before we blur the element. |
| 2069 | [self _endEditing]; |
| 2070 | |
| 2071 | auto shouldBlurFocusedElement = [&] { |
| 2072 | if (_keyboardDidRequestDismissal) |
| 2073 | return true; |
| 2074 | |
| 2075 | if (self._shouldUseLegacySelectPopoverDismissalBehavior) |
| 2076 | return true; |
| 2077 | |
| 2078 | if (reason == EndEditingReasonAccessoryDone) { |
| 2079 | if (_focusRequiresStrongPasswordAssistance) |
| 2080 | return true; |
| 2081 | |
| 2082 | if (PAL::currentUserInterfaceIdiomIsSmallScreen()) |
| 2083 | return true; |
| 2084 | } |
| 2085 | |
| 2086 | return false; |
| 2087 | }; |
| 2088 | |
| 2089 | if (shouldBlurFocusedElement()) { |
| 2090 | protect(_page)->blurFocusedElement(); |
| 2091 | // Don't wait for WebPageProxy::blurFocusedElement() to round-trip back to us to hide the keyboard |
| 2092 | // because we know that the user explicitly requested us to do so. |
| 2093 | [self _elementDidBlur]; |
| 2094 | } |
| 2095 | } |
| 2096 | |
| 2097 | [self _cancelInteraction]; |
| 2098 | |
| 2099 | BOOL shouldDeactivateSelection = [&]() -> BOOL { |
| 2100 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 2101 | if (reason == EndEditingReasonResigningFirstResponder) { |
| 2102 | // This logic is based on a similar check on macOS (in WebViewImpl::resignFirstResponder()) to |
| 2103 | // maintain the active selection when resigning first responder, if the new responder is in a |
| 2104 | // modal popover or panel. |
| 2105 | // FIXME: Ideally, this would additionally check that the new first responder corresponds to |
| 2106 | // a view underneath this view controller; however, there doesn't seem to be any way of doing |
| 2107 | // so at the moment. In lieu of this, we can at least check that the web view itself isn't |
| 2108 | // inside the popover. |
| 2109 | auto controller = self._wk_viewControllerForFullScreenPresentation; |
| 2110 | return [self isDescendantOfView:controller.viewIfLoaded] || controller.modalPresentationStyle != UIModalPresentationPopover; |
| 2111 | } |
| 2112 | #endif // PLATFORM(MACCATALYST) |
| 2113 | return YES__objc_yes; |
| 2114 | }(); |
| 2115 | |
| 2116 | if (shouldDeactivateSelection) |
| 2117 | [_textInteractionWrapper deactivateSelection]; |
| 2118 | |
| 2119 | [self stopDeferringInputViewUpdatesForAllSources]; |
| 2120 | } |
| 2121 | |
| 2122 | - (BOOL)resignFirstResponderForWebView |
| 2123 | { |
| 2124 | // FIXME: Maybe we should call resignFirstResponder on the superclass |
| 2125 | // and do nothing if the return value is NO. |
| 2126 | |
| 2127 | SetForScope resigningFirstResponderScope { _resigningFirstResponder, YES__objc_yes }; |
| 2128 | |
| 2129 | [self endEditingAndUpdateFocusAppearanceWithReason:EndEditingReasonResigningFirstResponder]; |
| 2130 | |
| 2131 | // If the user explicitly dismissed the keyboard then we will lose first responder |
| 2132 | // status only to gain it back again. Just don't resign in that case. |
| 2133 | if (_keyboardDidRequestDismissal) { |
| 2134 | _keyboardDidRequestDismissal = NO__objc_no; |
| 2135 | return NO__objc_no; |
| 2136 | } |
| 2137 | |
| 2138 | bool superDidResign = [super resignFirstResponder]; |
| 2139 | |
| 2140 | if (superDidResign) { |
| 2141 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 2142 | protect(_page)->activityStateDidChange(WebCore::ActivityState::IsFocused, WebKit::WebPageProxy::ActivityStateChangeDispatchMode::Immediate); |
| 2143 | |
| 2144 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 2145 | WebKit::UIGamepadProvider::singleton().viewBecameInactive(*protect(_page)); |
| 2146 | #endif |
| 2147 | |
| 2148 | _isHandlingActiveKeyEvent = NO__objc_no; |
| 2149 | _isHandlingActivePressesEvent = NO__objc_no; |
| 2150 | |
| 2151 | if (!_keyWebEventHandlers.isEmpty()) { |
| 2152 | RunLoop::mainSingleton().dispatch([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 2153 | RetainPtr strongSelf = weakSelf.get(); |
| 2154 | if (!strongSelf || [strongSelf isFirstResponder]) |
| 2155 | return; |
| 2156 | [strongSelf _cancelPendingKeyEventHandlers:YES__objc_yes]; |
| 2157 | }); |
| 2158 | } |
| 2159 | } |
| 2160 | |
| 2161 | return superDidResign; |
| 2162 | } |
| 2163 | |
| 2164 | - (void)cancelPointersForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer |
| 2165 | { |
| 2166 | NSMapTable<NSNumber *, UITouch *> *activeTouches = [_touchEventGestureRecognizer activeTouchesByIdentifier]; |
| 2167 | for (NSNumber *touchIdentifier in activeTouches) { |
| 2168 | UITouch *touch = [activeTouches objectForKey:touchIdentifier]; |
| 2169 | if ([touch.gestureRecognizers containsObject:gestureRecognizer]) |
| 2170 | protect(_page)->cancelPointer([touchIdentifier unsignedIntValue], WebCore::roundedIntPoint([touch locationInView:self])); |
| 2171 | } |
| 2172 | } |
| 2173 | |
| 2174 | - (std::optional<unsigned>)activeTouchIdentifierForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer |
| 2175 | { |
| 2176 | NSMapTable<NSNumber *, UITouch *> *activeTouches = [_touchEventGestureRecognizer activeTouchesByIdentifier]; |
| 2177 | for (NSNumber *touchIdentifier in activeTouches) { |
| 2178 | UITouch *touch = [activeTouches objectForKey:touchIdentifier]; |
| 2179 | if ([touch.gestureRecognizers containsObject:gestureRecognizer]) |
| 2180 | return [touchIdentifier unsignedIntValue]; |
| 2181 | } |
| 2182 | return std::nullopt; |
| 2183 | } |
| 2184 | |
| 2185 | - (BOOL)_touchEventsMustRequireGestureRecognizerToFail:(UIGestureRecognizer *)gestureRecognizer |
| 2186 | { |
| 2187 | auto webView = self.webView; |
| 2188 | if ([webView _isNavigationSwipeGestureRecognizer:gestureRecognizer]) |
| 2189 | return YES__objc_yes; |
| 2190 | |
| 2191 | id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([webView UIDelegate]); |
| 2192 | return gestureRecognizer.view == webView && [uiDelegate respondsToSelector:@selector(_webView:touchEventsMustRequireGestureRecognizerToFail:)] |
| 2193 | && [uiDelegate _webView:webView touchEventsMustRequireGestureRecognizerToFail:gestureRecognizer]; |
| 2194 | } |
| 2195 | |
| 2196 | - (BOOL)_gestureRecognizerCanBePreventedByTouchEvents:(UIGestureRecognizer *)gestureRecognizer |
| 2197 | { |
| 2198 | id<WKUIDelegatePrivate> uiDelegate = static_cast<id<WKUIDelegatePrivate>>([self.webView UIDelegate]); |
| 2199 | return [uiDelegate respondsToSelector:@selector(_webView:gestureRecognizerCanBePreventedByTouchEvents:)] && [uiDelegate _webView:self.webView gestureRecognizerCanBePreventedByTouchEvents:gestureRecognizer]; |
| 2200 | } |
| 2201 | |
| 2202 | - (void)_touchEventsRecognized |
| 2203 | { |
| 2204 | if (!protect(_page)->hasRunningProcess()) |
| 2205 | return; |
| 2206 | |
| 2207 | auto& lastTouchEvent = [_touchEventGestureRecognizer lastTouchEvent]; |
| 2208 | _lastInteractionLocation = lastTouchEvent.locationInRootViewCoordinates; |
| 2209 | |
| 2210 | if (lastTouchEvent.type == WebKit::WKTouchEventType::Begin) { |
| 2211 | if (!_failedTouchStartDeferringGestures) |
| 2212 | _failedTouchStartDeferringGestures = { { } }; |
| 2213 | |
| 2214 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 2215 | _layerTreeTransactionIdAtLastInteractionStart = protect(downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()))->lastCommittedMainFrameLayerTreeTransactionID(); |
| 2216 | |
| 2217 | #if ENABLE(TOUCH_EVENTS)(defined 0 && 0) |
| 2218 | _page->didBeginTouchPoint(lastTouchEvent.locationInRootViewCoordinates); |
| 2219 | #endif |
| 2220 | |
| 2221 | WebKit::InteractionInformationRequest positionInformationRequest { WebCore::IntPoint(_lastInteractionLocation) }; |
| 2222 | [self doAfterPositionInformationUpdate:[assistant = WeakObjCPtr<WKActionSheetAssistant>(_actionSheetAssistant.get())] (WebKit::InteractionInformationAtPosition information) { |
| 2223 | [assistant.get() interactionDidStartWithPositionInformation:information]; |
| 2224 | } forRequest:positionInformationRequest]; |
| 2225 | } |
| 2226 | |
| 2227 | #if ENABLE(TOUCH_EVENTS)(defined 0 && 0) |
| 2228 | WebKit::NativeWebTouchEvent nativeWebTouchEvent { lastTouchEvent, [_touchEventGestureRecognizer modifierFlags] }; |
| 2229 | nativeWebTouchEvent.setCanPreventNativeGestures(_touchEventsCanPreventNativeGestures || [_touchEventGestureRecognizer isDefaultPrevented]); |
| 2230 | |
| 2231 | [self _handleTouchActionsForTouchEvent:nativeWebTouchEvent]; |
| 2232 | |
| 2233 | if (_touchEventsCanPreventNativeGestures) |
| 2234 | _page->handlePreventableTouchEvent(nativeWebTouchEvent); |
| 2235 | else |
| 2236 | _page->handleUnpreventableTouchEvent(nativeWebTouchEvent); |
| 2237 | |
| 2238 | if (nativeWebTouchEvent.allTouchPointsAreReleased()) { |
| 2239 | _touchEventsCanPreventNativeGestures = YES__objc_yes; |
| 2240 | |
| 2241 | if (!_page->isScrollingOrZooming()) |
| 2242 | [self _resetPanningPreventionFlags]; |
| 2243 | |
| 2244 | if (nativeWebTouchEvent.isPotentialTap() && self.hasHiddenContentEditable && self._hasFocusedElement && !self.window.keyWindow) |
| 2245 | [self.window makeKeyWindow]; |
| 2246 | |
| 2247 | auto stopDeferringNativeGesturesIfNeeded = [] (WKDeferringGestureRecognizer *gestureRecognizer) { |
| 2248 | if (gestureRecognizer.state == UIGestureRecognizerStatePossible) |
| 2249 | gestureRecognizer.state = UIGestureRecognizerStateFailed; |
| 2250 | }; |
| 2251 | |
| 2252 | if (!_page->isHandlingPreventableTouchStart()) { |
| 2253 | for (WKDeferringGestureRecognizer *gestureRecognizer in self._touchStartDeferringGestures) |
| 2254 | stopDeferringNativeGesturesIfNeeded(gestureRecognizer); |
| 2255 | } |
| 2256 | |
| 2257 | if (!_page->isHandlingPreventableTouchMove()) |
| 2258 | stopDeferringNativeGesturesIfNeeded(_touchMoveDeferringGestureRecognizer.get()); |
| 2259 | |
| 2260 | if (!_page->isHandlingPreventableTouchEnd()) { |
| 2261 | for (WKDeferringGestureRecognizer *gestureRecognizer in self._touchEndDeferringGestures) |
| 2262 | stopDeferringNativeGesturesIfNeeded(gestureRecognizer); |
| 2263 | } |
| 2264 | |
| 2265 | _failedTouchStartDeferringGestures = std::nullopt; |
| 2266 | } |
| 2267 | #endif // ENABLE(TOUCH_EVENTS) |
| 2268 | } |
| 2269 | |
| 2270 | - (void)_touchEventsGestureRecognizerReset |
| 2271 | { |
| 2272 | protect(_page)->resetPointerCapture(); |
| 2273 | } |
| 2274 | |
| 2275 | #if ENABLE(TOUCH_EVENTS)(defined 0 && 0) |
| 2276 | - (void)_handleTouchActionsForTouchEvent:(const WebKit::NativeWebTouchEvent&)touchEvent |
| 2277 | { |
| 2278 | auto* scrollingCoordinator = downcast<WebKit::RemoteScrollingCoordinatorProxyIOS>(_page->scrollingCoordinatorProxy()); |
| 2279 | if (!scrollingCoordinator) |
| 2280 | return; |
| 2281 | |
| 2282 | for (const auto& touchPoint : touchEvent.touchPoints()) { |
| 2283 | auto phase = touchPoint.phase(); |
| 2284 | if (phase == WebKit::WebPlatformTouchPoint::State::Pressed) { |
| 2285 | auto touchActions = WebKit::touchActionsForPoint(self, WebCore::roundedIntPoint(touchPoint.locationInRootView())); |
| 2286 | LOG_WITH_STREAM(UIHitTesting, stream << "touchActionsForPoint " << touchPoint.locationInRootView() << " found " << touchActions)((void)0); |
| 2287 | if (!touchActions || touchActions.contains(WebCore::TouchAction::Auto)) |
| 2288 | continue; |
| 2289 | [_touchActionGestureRecognizer setTouchActions:touchActions forTouchIdentifier:touchPoint.identifier()]; |
| 2290 | scrollingCoordinator->setTouchActionsForTouchIdentifier(touchActions, touchPoint.identifier()); |
| 2291 | _preventsPanningInXAxis = !touchActions.containsAny({ WebCore::TouchAction::PanX, WebCore::TouchAction::Manipulation }); |
| 2292 | _preventsPanningInYAxis = !touchActions.containsAny({ WebCore::TouchAction::PanY, WebCore::TouchAction::Manipulation }); |
| 2293 | } else if (phase == WebKit::WebPlatformTouchPoint::State::Released || phase == WebKit::WebPlatformTouchPoint::State::Cancelled) { |
| 2294 | [_touchActionGestureRecognizer clearTouchActionsForTouchIdentifier:touchPoint.identifier()]; |
| 2295 | scrollingCoordinator->clearTouchActionsForTouchIdentifier(touchPoint.identifier()); |
| 2296 | } |
| 2297 | } |
| 2298 | } |
| 2299 | #endif // ENABLE(TOUCH_EVENTS) |
| 2300 | |
| 2301 | - (BOOL)_isTouchActionSwipeGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer |
| 2302 | { |
| 2303 | return gestureRecognizer == _touchActionLeftSwipeGestureRecognizer || gestureRecognizer == _touchActionRightSwipeGestureRecognizer || gestureRecognizer == _touchActionUpSwipeGestureRecognizer || gestureRecognizer == _touchActionDownSwipeGestureRecognizer; |
| 2304 | } |
| 2305 | |
| 2306 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveEvent:(UIEvent *)event |
| 2307 | { |
| 2308 | if ([self _isTouchActionSwipeGestureRecognizer:gestureRecognizer]) |
| 2309 | return event.type != UIEventTypeScroll; |
| 2310 | return YES__objc_yes; |
| 2311 | } |
| 2312 | |
| 2313 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch |
| 2314 | { |
| 2315 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 2316 | if (gestureRecognizer == _lookupGestureRecognizer) |
| 2317 | return YES__objc_yes; |
| 2318 | #endif |
| 2319 | |
| 2320 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 2321 | auto isMouseGestureRecognizer = gestureRecognizer == [_mouseInteraction mouseTouchGestureRecognizer]; |
| 2322 | if (!isMouseGestureRecognizer && [_mouseInteraction mouseTouch] == touch && touch._isPointerTouch) |
| 2323 | return NO__objc_no; |
| 2324 | |
| 2325 | if (isMouseGestureRecognizer && !touch._isPointerTouch) |
| 2326 | return NO__objc_no; |
| 2327 | |
| 2328 | if (gestureRecognizer == _doubleTapGestureRecognizer || gestureRecognizer == _nonBlockingDoubleTapGestureRecognizer) |
| 2329 | return touch.type != UITouchTypeIndirectPointer; |
| 2330 | #endif |
| 2331 | |
| 2332 | if ([self _isTouchActionSwipeGestureRecognizer:gestureRecognizer]) { |
| 2333 | // We update the enabled state of the various swipe gesture recognizers such that if we have a unidirectional touch-action |
| 2334 | // specified (only pan-x or only pan-y) we enable the two recognizers in the opposite axis to prevent scrolling from starting |
| 2335 | // if the initial gesture is such a swipe. Since the recognizers are specified to use a single finger for recognition, we don't |
| 2336 | // need to worry about the case where there may be more than a single touch for a given UIScrollView. |
| 2337 | auto touchLocation = [touch locationInView:self]; |
| 2338 | auto touchActions = WebKit::touchActionsForPoint(self, WebCore::roundedIntPoint(touchLocation)); |
| 2339 | LOG_WITH_STREAM(UIHitTesting, stream << "gestureRecognizer:shouldReceiveTouch: at " << WebCore::FloatPoint(touchLocation) << " - touch actions " << touchActions)((void)0); |
| 2340 | if (gestureRecognizer == _touchActionLeftSwipeGestureRecognizer || gestureRecognizer == _touchActionRightSwipeGestureRecognizer) |
| 2341 | return touchActions == WebCore::TouchAction::PanY; |
| 2342 | return touchActions == WebCore::TouchAction::PanX; |
| 2343 | } |
| 2344 | |
| 2345 | return YES__objc_yes; |
| 2346 | } |
| 2347 | |
| 2348 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePress:(UIPress *)press |
| 2349 | { |
| 2350 | return YES__objc_yes; |
| 2351 | } |
| 2352 | |
| 2353 | #pragma mark - WKTouchActionGestureRecognizerDelegate implementation |
| 2354 | |
| 2355 | - (BOOL)gestureRecognizerMayPanWebView:(UIGestureRecognizer *)gestureRecognizer |
| 2356 | { |
| 2357 | // The gesture recognizer is the main UIScrollView's UIPanGestureRecognizer. |
| 2358 | if (gestureRecognizer == [_webView.get() scrollView].panGestureRecognizer) |
| 2359 | return YES__objc_yes; |
| 2360 | |
| 2361 | // The gesture recognizer is a child UIScrollView's UIPanGestureRecognizer created by WebKit. |
| 2362 | if (gestureRecognizer.view && [gestureRecognizer.view isKindOfClass:[WKChildScrollView class]]) |
| 2363 | return YES__objc_yes; |
| 2364 | |
| 2365 | return NO__objc_no; |
| 2366 | } |
| 2367 | |
| 2368 | - (BOOL)gestureRecognizerMayPinchToZoomWebView:(UIGestureRecognizer *)gestureRecognizer |
| 2369 | { |
| 2370 | // The gesture recognizer is the main UIScrollView's UIPinchGestureRecognizer. |
| 2371 | if (gestureRecognizer == [_webView.get() scrollView].pinchGestureRecognizer) |
| 2372 | return YES__objc_yes; |
| 2373 | |
| 2374 | // The gesture recognizer is another UIPinchGestureRecognizer known to lead to pinch-to-zoom. |
| 2375 | if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) { |
| 2376 | if (auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate)) { |
| 2377 | if ([uiDelegate respondsToSelector:@selector(_webView:gestureRecognizerCouldPinch:)]) |
| 2378 | return [uiDelegate _webView:self.webView gestureRecognizerCouldPinch:gestureRecognizer]; |
| 2379 | } |
| 2380 | } |
| 2381 | return NO__objc_no; |
| 2382 | } |
| 2383 | |
| 2384 | - (BOOL)gestureRecognizerMayDoubleTapToZoomWebView:(UIGestureRecognizer *)gestureRecognizer |
| 2385 | { |
| 2386 | return gestureRecognizer == _doubleTapGestureRecognizer || gestureRecognizer == _twoFingerDoubleTapGestureRecognizer; |
| 2387 | } |
| 2388 | |
| 2389 | - (NSMapTable<NSNumber *, UITouch *> *)touchActionActiveTouches |
| 2390 | { |
| 2391 | return [_touchEventGestureRecognizer activeTouchesByIdentifier]; |
| 2392 | } |
| 2393 | |
| 2394 | - (void)_resetPanningPreventionFlags |
| 2395 | { |
| 2396 | _preventsPanningInXAxis = NO__objc_no; |
| 2397 | _preventsPanningInYAxis = NO__objc_no; |
| 2398 | } |
| 2399 | |
| 2400 | - (void)_inspectorNodeSearchRecognized:(UIGestureRecognizer *)gestureRecognizer |
| 2401 | { |
| 2402 | ASSERT(_inspectorNodeSearchEnabled)((void)0); |
| 2403 | [self _resetIsDoubleTapPending]; |
| 2404 | |
| 2405 | CGPoint point = [gestureRecognizer locationInView:self]; |
| 2406 | |
| 2407 | switch (gestureRecognizer.state) { |
| 2408 | case UIGestureRecognizerStateBegan: |
| 2409 | case UIGestureRecognizerStateChanged: |
| 2410 | protect(_page)->inspectorNodeSearchMovedToPosition(point); |
| 2411 | break; |
| 2412 | case UIGestureRecognizerStateEnded: |
| 2413 | case UIGestureRecognizerStateCancelled: |
| 2414 | default: // To ensure we turn off node search. |
| 2415 | protect(_page)->inspectorNodeSearchEndedAtPosition(point); |
| 2416 | break; |
| 2417 | } |
| 2418 | } |
| 2419 | |
| 2420 | static WebCore::FloatQuad inflateQuad(const WebCore::FloatQuad& quad, float inflateSize) |
| 2421 | { |
| 2422 | // We sort the output points like this (as expected by the highlight view): |
| 2423 | // p2------p3 |
| 2424 | // | | |
| 2425 | // p1------p4 |
| 2426 | |
| 2427 | // 1) Sort the points horizontally. |
| 2428 | std::array points { quad.p1(), quad.p4(), quad.p2(), quad.p3() }; |
| 2429 | if (points[0].x() > points[1].x()) |
| 2430 | std::swap(points[0], points[1]); |
| 2431 | if (points[2].x() > points[3].x()) |
| 2432 | std::swap(points[2], points[3]); |
| 2433 | |
| 2434 | if (points[0].x() > points[2].x()) |
| 2435 | std::swap(points[0], points[2]); |
| 2436 | if (points[1].x() > points[3].x()) |
| 2437 | std::swap(points[1], points[3]); |
| 2438 | |
| 2439 | if (points[1].x() > points[2].x()) |
| 2440 | std::swap(points[1], points[2]); |
| 2441 | |
| 2442 | // 2) Swap them vertically to have the output points [p2, p1, p3, p4]. |
| 2443 | if (points[1].y() < points[0].y()) |
| 2444 | std::swap(points[0], points[1]); |
| 2445 | if (points[3].y() < points[2].y()) |
| 2446 | std::swap(points[2], points[3]); |
| 2447 | |
| 2448 | // 3) Adjust the positions. |
| 2449 | points[0].move(-inflateSize, -inflateSize); |
| 2450 | points[1].move(-inflateSize, inflateSize); |
| 2451 | points[2].move(inflateSize, -inflateSize); |
| 2452 | points[3].move(inflateSize, inflateSize); |
| 2453 | |
| 2454 | return WebCore::FloatQuad(points[1], points[0], points[2], points[3]); |
| 2455 | } |
| 2456 | |
| 2457 | #if ENABLE(TOUCH_EVENTS)(defined 0 && 0) |
| 2458 | - (void)_touchEvent:(const WebKit::WebTouchEvent&)touchEvent preventsNativeGestures:(BOOL)preventsNativeGesture |
| 2459 | { |
| 2460 | if (!preventsNativeGesture || ![_touchEventGestureRecognizer isDispatchingTouchEvents]) |
| 2461 | return; |
| 2462 | |
| 2463 | _longPressCanClick = NO__objc_no; |
| 2464 | _touchEventsCanPreventNativeGestures = NO__objc_no; |
| 2465 | [_touchEventGestureRecognizer setDefaultPrevented:YES__objc_yes]; |
| 2466 | } |
| 2467 | #endif |
| 2468 | |
| 2469 | - (WKTouchEventsGestureRecognizer *)touchEventGestureRecognizer |
| 2470 | { |
| 2471 | return _touchEventGestureRecognizer.get(); |
| 2472 | } |
| 2473 | |
| 2474 | - (WebKit::GestureRecognizerConsistencyEnforcer&)gestureRecognizerConsistencyEnforcer |
| 2475 | { |
| 2476 | if (!_gestureRecognizerConsistencyEnforcer) |
| 2477 | lazyInitialize(_gestureRecognizerConsistencyEnforcer, makeUniqueWithoutRefCountedCheck<WebKit::GestureRecognizerConsistencyEnforcer>(self)); |
| 2478 | |
| 2479 | return *_gestureRecognizerConsistencyEnforcer; |
| 2480 | } |
| 2481 | |
| 2482 | - (NSArray<WKDeferringGestureRecognizer *> *)deferringGestures |
| 2483 | { |
| 2484 | auto gestures = [NSMutableArray arrayWithCapacity:7]; |
| 2485 | [gestures addObjectsFromArray:self._touchStartDeferringGestures]; |
| 2486 | [gestures addObjectsFromArray:self._touchEndDeferringGestures]; |
| 2487 | if (_touchMoveDeferringGestureRecognizer) |
| 2488 | [gestures addObject:_touchMoveDeferringGestureRecognizer.get()]; |
| 2489 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 2490 | if (_imageAnalysisDeferringGestureRecognizer) |
| 2491 | [gestures addObject:_imageAnalysisDeferringGestureRecognizer.get()]; |
| 2492 | #endif |
| 2493 | return gestures; |
| 2494 | } |
| 2495 | |
| 2496 | static void appendRecognizerIfNonNull(RetainPtr<NSMutableArray>& array, const RetainPtr<WKDeferringGestureRecognizer>& recognizer) |
| 2497 | { |
| 2498 | if (recognizer) |
| 2499 | [array addObject:recognizer.get()]; |
| 2500 | } |
| 2501 | |
| 2502 | - (NSArray<WKDeferringGestureRecognizer *> *)_touchStartDeferringGestures |
| 2503 | { |
| 2504 | RetainPtr recognizers = adoptNS([[NSMutableArray alloc] initWithCapacity:3]); |
| 2505 | appendRecognizerIfNonNull(recognizers, _touchStartDeferringGestureRecognizerForImmediatelyResettableGestures); |
| 2506 | appendRecognizerIfNonNull(recognizers, _touchStartDeferringGestureRecognizerForDelayedResettableGestures); |
| 2507 | appendRecognizerIfNonNull(recognizers, _touchStartDeferringGestureRecognizerForSyntheticTapGestures); |
| 2508 | return recognizers.autorelease(); |
| 2509 | } |
| 2510 | |
| 2511 | - (NSArray<WKDeferringGestureRecognizer *> *)_touchEndDeferringGestures |
| 2512 | { |
| 2513 | RetainPtr recognizers = adoptNS([[NSMutableArray alloc] initWithCapacity:3]); |
| 2514 | appendRecognizerIfNonNull(recognizers, _touchEndDeferringGestureRecognizerForImmediatelyResettableGestures); |
| 2515 | appendRecognizerIfNonNull(recognizers, _touchEndDeferringGestureRecognizerForDelayedResettableGestures); |
| 2516 | appendRecognizerIfNonNull(recognizers, _touchEndDeferringGestureRecognizerForSyntheticTapGestures); |
| 2517 | return recognizers.autorelease(); |
| 2518 | } |
| 2519 | |
| 2520 | - (void)_doneDeferringTouchStart:(BOOL)preventNativeGestures |
| 2521 | { |
| 2522 | for (WKDeferringGestureRecognizer *gestureRecognizer in self._touchStartDeferringGestures) { |
| 2523 | [gestureRecognizer endDeferral:preventNativeGestures ? WebKit::ShouldPreventGestures::Yes : WebKit::ShouldPreventGestures::No]; |
| 2524 | if (_failedTouchStartDeferringGestures && !preventNativeGestures) |
| 2525 | _failedTouchStartDeferringGestures->add(gestureRecognizer); |
| 2526 | } |
| 2527 | } |
| 2528 | |
| 2529 | - (void)_doneDeferringTouchMove:(BOOL)preventNativeGestures |
| 2530 | { |
| 2531 | [_touchMoveDeferringGestureRecognizer endDeferral:preventNativeGestures ? WebKit::ShouldPreventGestures::Yes : WebKit::ShouldPreventGestures::No]; |
| 2532 | } |
| 2533 | |
| 2534 | - (void)_doneDeferringTouchEnd:(BOOL)preventNativeGestures |
| 2535 | { |
| 2536 | for (WKDeferringGestureRecognizer *gesture in self._touchEndDeferringGestures) |
| 2537 | [gesture endDeferral:preventNativeGestures ? WebKit::ShouldPreventGestures::Yes : WebKit::ShouldPreventGestures::No]; |
| 2538 | } |
| 2539 | |
| 2540 | - (BOOL)_isTouchStartDeferringGesture:(WKDeferringGestureRecognizer *)gesture |
| 2541 | { |
| 2542 | return gesture == _touchStartDeferringGestureRecognizerForSyntheticTapGestures || gesture == _touchStartDeferringGestureRecognizerForImmediatelyResettableGestures |
| 2543 | || gesture == _touchStartDeferringGestureRecognizerForDelayedResettableGestures; |
| 2544 | } |
| 2545 | |
| 2546 | - (BOOL)_isTouchEndDeferringGesture:(WKDeferringGestureRecognizer *)gesture |
| 2547 | { |
| 2548 | return gesture == _touchEndDeferringGestureRecognizerForSyntheticTapGestures || gesture == _touchEndDeferringGestureRecognizerForImmediatelyResettableGestures |
| 2549 | || gesture == _touchEndDeferringGestureRecognizerForDelayedResettableGestures; |
| 2550 | } |
| 2551 | |
| 2552 | static inline WebCore::FloatSize tapHighlightBorderRadius(WebCore::FloatSize borderRadii, CGFloat borderRadiusScale) |
| 2553 | { |
| 2554 | auto inflatedSize = borderRadii * borderRadiusScale; |
| 2555 | inflatedSize.expand(minimumTapHighlightRadius, minimumTapHighlightRadius); |
| 2556 | return inflatedSize; |
| 2557 | } |
| 2558 | |
| 2559 | - (void)_updateTapHighlight |
| 2560 | { |
| 2561 | if (![_tapHighlightView superview]) |
| 2562 | return; |
| 2563 | |
| 2564 | [_tapHighlightView setColor:cocoaColor(_tapHighlightInformation.color).get()]; |
| 2565 | |
| 2566 | auto& highlightedQuads = _tapHighlightInformation.quads; |
| 2567 | bool allRectilinear = true; |
| 2568 | for (auto& quad : highlightedQuads) { |
| 2569 | if (!quad.isRectilinear()) { |
| 2570 | allRectilinear = false; |
| 2571 | break; |
| 2572 | } |
| 2573 | } |
| 2574 | |
| 2575 | auto selfScale = self.layer.transform.m11; |
| 2576 | if (allRectilinear) { |
| 2577 | float deviceScaleFactor = protect(_page)->deviceScaleFactor(); |
| 2578 | auto rects = createNSArray(highlightedQuads, [&] (auto& quad) { |
| 2579 | auto boundingBox = quad.boundingBox(); |
| 2580 | boundingBox.scale(selfScale); |
| 2581 | boundingBox.inflate(minimumTapHighlightRadius); |
| 2582 | return [NSValue valueWithCGRect:encloseRectToDevicePixels(boundingBox, deviceScaleFactor)]; |
| 2583 | }); |
| 2584 | [_tapHighlightView setFrames:highlightedQuads.map([&](auto& quad) { |
| 2585 | auto boundingBox = quad.boundingBox(); |
| 2586 | boundingBox.scale(selfScale); |
| 2587 | boundingBox.inflate(minimumTapHighlightRadius); |
| 2588 | return boundingBox; |
| 2589 | })]; |
| 2590 | } else { |
| 2591 | auto quads = adoptNS([[NSMutableArray alloc] initWithCapacity:highlightedQuads.size() * 4]); |
| 2592 | for (auto quad : highlightedQuads) { |
| 2593 | quad.scale(selfScale); |
| 2594 | auto extendedQuad = inflateQuad(quad, minimumTapHighlightRadius); |
| 2595 | [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p1()]]; |
| 2596 | [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p2()]]; |
| 2597 | [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p3()]]; |
| 2598 | [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p4()]]; |
| 2599 | } |
| 2600 | |
| 2601 | auto inflatedHighlightQuads = highlightedQuads.map([&](auto quad) { |
| 2602 | quad.scale(selfScale); |
| 2603 | return inflateQuad(quad, minimumTapHighlightRadius); |
| 2604 | }); |
| 2605 | [_tapHighlightView setQuads:WTF::move(inflatedHighlightQuads) boundaryRect:protect(_page)->exposedContentRect()]; |
| 2606 | } |
| 2607 | |
| 2608 | [_tapHighlightView setCornerRadii:WebCore::CornerRadii { |
| 2609 | tapHighlightBorderRadius(_tapHighlightInformation.topLeftRadius, selfScale), |
| 2610 | tapHighlightBorderRadius(_tapHighlightInformation.topRightRadius, selfScale), |
| 2611 | tapHighlightBorderRadius(_tapHighlightInformation.bottomLeftRadius, selfScale), |
| 2612 | tapHighlightBorderRadius(_tapHighlightInformation.bottomRightRadius, selfScale) |
| 2613 | }]; |
| 2614 | } |
| 2615 | |
| 2616 | - (CGRect)tapHighlightViewRect |
| 2617 | { |
| 2618 | return [_tapHighlightView frame]; |
| 2619 | } |
| 2620 | |
| 2621 | - (UIGestureRecognizer *)imageAnalysisGestureRecognizer |
| 2622 | { |
| 2623 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 2624 | return _imageAnalysisGestureRecognizer.get(); |
| 2625 | #else |
| 2626 | return nilnullptr; |
| 2627 | #endif |
| 2628 | } |
| 2629 | |
| 2630 | - (void)_showTapHighlight |
| 2631 | { |
| 2632 | auto shouldPaintTapHighlight = [&](const WebCore::FloatRect& rect) { |
| 2633 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 2634 | UNUSED_PARAM(rect)(void)rect; |
| 2635 | return NO__objc_no; |
| 2636 | #else |
| 2637 | if (_tapHighlightInformation.nodeHasBuiltInClickHandling) |
| 2638 | return true; |
| 2639 | |
| 2640 | static const float highlightPaintThreshold = 0.3; // 30% |
| 2641 | float highlightArea = 0; |
| 2642 | for (auto highlightQuad : _tapHighlightInformation.quads) { |
| 2643 | auto boundingBox = highlightQuad.boundingBox(); |
| 2644 | highlightArea += boundingBox.area(); |
| 2645 | if (boundingBox.width() > (rect.width() * highlightPaintThreshold) || boundingBox.height() > (rect.height() * highlightPaintThreshold)) |
| 2646 | return false; |
| 2647 | } |
| 2648 | return highlightArea < rect.area() * highlightPaintThreshold; |
| 2649 | #endif |
| 2650 | }; |
| 2651 | |
| 2652 | if (!shouldPaintTapHighlight(protect(_page)->unobscuredContentRect()) && !_showDebugTapHighlightsForFastClicking) |
| 2653 | return; |
| 2654 | |
| 2655 | if (!_tapHighlightView) { |
| 2656 | _tapHighlightView = adoptNS([[WKTapHighlightView alloc] initWithFrame:CGRectZero]); |
| 2657 | [_tapHighlightView setUserInteractionEnabled:NO__objc_no]; |
| 2658 | [_tapHighlightView setOpaque:NO__objc_no]; |
| 2659 | [_tapHighlightView setMinimumCornerRadius:minimumTapHighlightRadius]; |
| 2660 | } |
| 2661 | [_tapHighlightView setAlpha:1]; |
| 2662 | [_interactionViewsContainerView addSubview:_tapHighlightView.get()]; |
| 2663 | [self _updateTapHighlight]; |
| 2664 | } |
| 2665 | |
| 2666 | - (void)_didGetTapHighlightForRequest:(WebKit::TapIdentifier)requestID color:(const WebCore::Color&)color quads:(const Vector<WebCore::FloatQuad>&)highlightedQuads topLeftRadius:(const WebCore::IntSize&)topLeftRadius topRightRadius:(const WebCore::IntSize&)topRightRadius bottomLeftRadius:(const WebCore::IntSize&)bottomLeftRadius bottomRightRadius:(const WebCore::IntSize&)bottomRightRadius nodeHasBuiltInClickHandling:(BOOL)nodeHasBuiltInClickHandling |
| 2667 | { |
| 2668 | if (!_isTapHighlightIDValid || _latestTapID != requestID) |
| 2669 | return; |
| 2670 | |
| 2671 | if (self._hasFocusedElement && _positionInformation.elementContext && _positionInformation.elementContext->isSameElement(_focusedElementInformation.elementContext)) |
| 2672 | return; |
| 2673 | |
| 2674 | _isTapHighlightIDValid = NO__objc_no; |
| 2675 | |
| 2676 | _tapHighlightInformation.quads = highlightedQuads; |
| 2677 | _tapHighlightInformation.topLeftRadius = topLeftRadius; |
| 2678 | _tapHighlightInformation.topRightRadius = topRightRadius; |
| 2679 | _tapHighlightInformation.bottomLeftRadius = bottomLeftRadius; |
| 2680 | _tapHighlightInformation.bottomRightRadius = bottomRightRadius; |
| 2681 | _tapHighlightInformation.nodeHasBuiltInClickHandling = nodeHasBuiltInClickHandling; |
| 2682 | if (_showDebugTapHighlightsForFastClicking) |
| 2683 | _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:![_doubleTapGestureRecognizer isEnabled]]; |
| 2684 | else |
| 2685 | _tapHighlightInformation.color = color; |
| 2686 | |
| 2687 | if (_potentialTapInProgress) { |
| 2688 | _hasTapHighlightForPotentialTap = YES__objc_yes; |
| 2689 | return; |
| 2690 | } |
| 2691 | |
| 2692 | [self _showTapHighlight]; |
| 2693 | if (_isExpectingFastSingleTapCommit) { |
| 2694 | [self _finishInteraction]; |
| 2695 | if (!_potentialTapInProgress) |
| 2696 | _isExpectingFastSingleTapCommit = NO__objc_no; |
| 2697 | } |
| 2698 | } |
| 2699 | |
| 2700 | - (BOOL)_mayDisableDoubleTapGesturesDuringSingleTap |
| 2701 | { |
| 2702 | return _potentialTapInProgress; |
| 2703 | } |
| 2704 | |
| 2705 | - (void)_disableDoubleTapGesturesDuringTapIfNecessary:(WebKit::TapIdentifier)requestID |
| 2706 | { |
| 2707 | if (_latestTapID != requestID) |
| 2708 | return; |
| 2709 | |
| 2710 | [self _setDoubleTapGesturesEnabled:NO__objc_no]; |
| 2711 | } |
| 2712 | |
| 2713 | - (void)_handleSmartMagnificationInformationForPotentialTap:(WebKit::TapIdentifier)requestID renderRect:(const WebCore::FloatRect&)renderRect fitEntireRect:(BOOL)fitEntireRect viewportMinimumScale:(double)viewportMinimumScale viewportMaximumScale:(double)viewportMaximumScale nodeIsRootLevel:(BOOL)nodeIsRootLevel nodeIsPluginElement:(BOOL)nodeIsPluginElement |
| 2714 | { |
| 2715 | Ref preferences = _page->preferences(); |
| 2716 | |
| 2717 | ASSERT(preferences->fasterClicksEnabled())((void)0); |
| 2718 | if (!_potentialTapInProgress) |
| 2719 | return; |
| 2720 | |
| 2721 | // We check both the system preference and the page preference, because we only want this |
| 2722 | // to apply in "desktop" mode. |
| 2723 | if (preferences->preferFasterClickOverDoubleTap() && _page->preferFasterClickOverDoubleTap()) { |
| 2724 | RELEASE_LOG(ViewGestures, "Potential tap found an element and fast taps are preferred. Trigger click. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Potential tap found an element and fast taps are preferred. Trigger click. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG155" ) = "Potential tap found an element and fast taps are preferred. Trigger click. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Potential tap found an element and fast taps are preferred. Trigger click. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Potential tap found an element and fast taps are preferred. Trigger click. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2725 | if (preferences->zoomOnDoubleTapWhenRoot() && nodeIsRootLevel) { |
| 2726 | RELEASE_LOG(ViewGestures, "The click handler was on a root-level element, so don't disable double-tap. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("The click handler was on a root-level element, so don't disable double-tap. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG156" ) = "The click handler was on a root-level element, so don't disable double-tap. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("The click handler was on a root-level element, so don't disable double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "The click handler was on a root-level element, so don't disable double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2727 | return; |
| 2728 | } |
| 2729 | |
| 2730 | if (preferences->alwaysZoomOnDoubleTap()) { |
| 2731 | RELEASE_LOG(ViewGestures, "DTTZ is forced on, so don't disable double-tap. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("DTTZ is forced on, so don't disable double-tap. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG157" ) = "DTTZ is forced on, so don't disable double-tap. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("DTTZ is forced on, so don't disable double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "DTTZ is forced on, so don't disable double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2732 | return; |
| 2733 | } |
| 2734 | |
| 2735 | if (nodeIsPluginElement) { |
| 2736 | RELEASE_LOG(ViewGestures, "The click handler was on a plugin element, so don't disable double-tap. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("The click handler was on a plugin element, so don't disable double-tap. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG158" ) = "The click handler was on a plugin element, so don't disable double-tap. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("The click handler was on a plugin element, so don't disable double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "The click handler was on a plugin element, so don't disable double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2737 | return; |
| 2738 | } |
| 2739 | |
| 2740 | RELEASE_LOG(ViewGestures, "Give preference to click by disabling double-tap. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Give preference to click by disabling double-tap. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG159" ) = "Give preference to click by disabling double-tap. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Give preference to click by disabling double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Give preference to click by disabling double-tap. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2741 | [self _setDoubleTapGesturesEnabled:NO__objc_no]; |
| 2742 | return; |
| 2743 | } |
| 2744 | |
| 2745 | auto currentScale = self._contentZoomScale; |
| 2746 | auto targetScale = protect(_smartMagnificationController)->zoomFactorForTargetRect(renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale); |
| 2747 | if (std::min(targetScale, currentScale) / std::max(targetScale, currentScale) > fasterTapSignificantZoomThreshold) { |
| 2748 | RELEASE_LOG(ViewGestures, "Potential tap would not cause a significant zoom. Trigger click. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Potential tap would not cause a significant zoom. Trigger click. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG160" ) = "Potential tap would not cause a significant zoom. Trigger click. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Potential tap would not cause a significant zoom. Trigger click. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Potential tap would not cause a significant zoom. Trigger click. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2749 | [self _setDoubleTapGesturesEnabled:NO__objc_no]; |
| 2750 | return; |
| 2751 | } |
| 2752 | RELEASE_LOG(ViewGestures, "Potential tap may cause significant zoom. Wait. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Potential tap may cause significant zoom. Wait. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG161" ) = "Potential tap may cause significant zoom. Wait. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Potential tap may cause significant zoom. Wait. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Potential tap may cause significant zoom. Wait. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 2753 | } |
| 2754 | |
| 2755 | - (void)_cancelLongPressGestureRecognizer |
| 2756 | { |
| 2757 | [_highlightLongPressGestureRecognizer cancel]; |
| 2758 | } |
| 2759 | |
| 2760 | - (void)_cancelTouchEventGestureRecognizer |
| 2761 | { |
| 2762 | [_touchEventGestureRecognizer cancel]; |
| 2763 | } |
| 2764 | |
| 2765 | - (void)_didScroll |
| 2766 | { |
| 2767 | [self _updateFrameOfContainerForContextMenuHintPreviewsIfNeeded]; |
| 2768 | |
| 2769 | [self _cancelLongPressGestureRecognizer]; |
| 2770 | [self _cancelInteraction]; |
| 2771 | } |
| 2772 | |
| 2773 | - (void)_scrollingNodeScrollingWillBegin:(std::optional<WebCore::ScrollingNodeID>)scrollingNodeID |
| 2774 | { |
| 2775 | [_textInteractionWrapper willStartScrollingOverflow:[self _scrollViewForScrollingNodeID:scrollingNodeID]]; |
| 2776 | } |
| 2777 | |
| 2778 | - (void)_scrollingNodeScrollingDidEnd:(std::optional<WebCore::ScrollingNodeID>)scrollingNodeID |
| 2779 | { |
| 2780 | if (auto& state = protect(_page)->editorState(); state.hasVisualData() && scrollingNodeID && scrollingNodeID == state.visualData->enclosingScrollingNodeID) { |
| 2781 | protect(_page)->scheduleFullEditorStateUpdate(); |
| 2782 | _waitingForEditorStateAfterScrollingSelectionContainer = YES__objc_yes; |
| 2783 | protect(_page)->callAfterNextPresentationUpdate([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 2784 | if (auto strongSelf = weakSelf.get()) |
| 2785 | strongSelf->_waitingForEditorStateAfterScrollingSelectionContainer = NO__objc_no; |
| 2786 | }); |
| 2787 | } |
| 2788 | |
| 2789 | // If scrolling ends before we've received a selection update, |
| 2790 | // we postpone showing the selection until the update is received. |
| 2791 | if (!_selectionNeedsUpdate) { |
| 2792 | _shouldRestoreSelection = YES__objc_yes; |
| 2793 | return; |
| 2794 | } |
| 2795 | [self _updateChangedSelection]; |
| 2796 | [_textInteractionWrapper didEndScrollingOverflow]; |
| 2797 | |
| 2798 | [_webView.get() _didFinishScrolling:[self _scrollViewForScrollingNodeID:scrollingNodeID]]; |
| 2799 | } |
| 2800 | |
| 2801 | - (BOOL)shouldShowAutomaticKeyboardUI |
| 2802 | { |
| 2803 | if (_focusedElementInformation.inputMode == WebCore::InputMode::None) |
| 2804 | return NO__objc_no; |
| 2805 | |
| 2806 | if (_focusedElementInformation.isFocusingWithDataListDropdown && !UIKeyboard.isInHardwareKeyboardMode) |
| 2807 | return NO__objc_no; |
| 2808 | |
| 2809 | return [self _shouldShowAutomaticKeyboardUIIgnoringInputMode]; |
| 2810 | } |
| 2811 | |
| 2812 | - (BOOL)_shouldShowAutomaticKeyboardUIIgnoringInputMode |
| 2813 | { |
| 2814 | if (_focusedElementInformation.isReadOnly) |
| 2815 | return NO__objc_no; |
| 2816 | |
| 2817 | switch (_focusedElementInformation.elementType) { |
| 2818 | case WebKit::InputType::None: |
| 2819 | case WebKit::InputType::Color: |
| 2820 | case WebKit::InputType::Drawing: |
| 2821 | case WebKit::InputType::Date: |
| 2822 | case WebKit::InputType::Month: |
| 2823 | case WebKit::InputType::DateTimeLocal: |
| 2824 | case WebKit::InputType::Time: |
| 2825 | #if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 2826 | case WebKit::InputType::Week: |
| 2827 | #endif |
| 2828 | return NO__objc_no; |
| 2829 | case WebKit::InputType::Select: { |
| 2830 | if (self._shouldUseContextMenusForFormControls) |
| 2831 | return NO__objc_no; |
| 2832 | return PAL::currentUserInterfaceIdiomIsSmallScreen(); |
| 2833 | } |
| 2834 | default: |
| 2835 | return YES__objc_yes; |
| 2836 | } |
| 2837 | return NO__objc_no; |
| 2838 | } |
| 2839 | |
| 2840 | - (BOOL)_disableAutomaticKeyboardUI |
| 2841 | { |
| 2842 | // Always enable automatic keyboard UI if we are not the first responder to avoid |
| 2843 | // interfering with other focused views (e.g. Find-in-page). |
| 2844 | return [self isFirstResponder] && ![self shouldShowAutomaticKeyboardUI]; |
| 2845 | } |
| 2846 | |
| 2847 | - (BOOL)_requiresKeyboardWhenFirstResponder |
| 2848 | { |
| 2849 | BOOL webViewIsEditable = [_webView.get() _isEditable]; |
| 2850 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 2851 | if (!webViewIsEditable && self.shouldUseAsyncInteractions) |
| 2852 | return [super _requiresKeyboardWhenFirstResponder]; |
| 2853 | #endif |
| 2854 | |
| 2855 | if (webViewIsEditable && !self._disableAutomaticKeyboardUI) |
| 2856 | return YES__objc_yes; |
| 2857 | |
| 2858 | // FIXME: We should add the logic to handle keyboard visibility during focus redirects. |
| 2859 | return [self _shouldShowAutomaticKeyboardUIIgnoringInputMode] || _seenHardwareKeyDownInNonEditableElement; |
| 2860 | } |
| 2861 | |
| 2862 | - (BOOL)_requiresKeyboardResetOnReload |
| 2863 | { |
| 2864 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 2865 | if (self.shouldUseAsyncInteractions) |
| 2866 | return [super _requiresKeyboardResetOnReload]; |
| 2867 | #endif |
| 2868 | |
| 2869 | return YES__objc_yes; |
| 2870 | } |
| 2871 | |
| 2872 | - (WebCore::FloatRect)rectToRevealWhenZoomingToFocusedElement |
| 2873 | { |
| 2874 | WebCore::IntRect elementInteractionRect; |
| 2875 | if (_focusedElementInformation.interactionRect.contains(_focusedElementInformation.lastInteractionLocation)) |
| 2876 | elementInteractionRect = { _focusedElementInformation.lastInteractionLocation, { 1, 1 } }; |
| 2877 | |
| 2878 | if (!mayContainSelectableText(_focusedElementInformation.elementType)) |
| 2879 | return elementInteractionRect; |
| 2880 | |
| 2881 | if (!protect(_page)->editorState().hasPostLayoutAndVisualData()) |
| 2882 | return elementInteractionRect; |
| 2883 | |
| 2884 | auto boundingRect = protect(_page)->selectionBoundingRectInRootViewCoordinates(); |
| 2885 | boundingRect.intersect(_focusedElementInformation.interactionRect); |
| 2886 | return boundingRect; |
| 2887 | } |
| 2888 | |
| 2889 | - (void)_keyboardWillShow |
| 2890 | { |
| 2891 | _waitingForKeyboardAppearanceAnimationToStart = NO__objc_no; |
| 2892 | |
| 2893 | if (_revealFocusedElementDeferrer) |
| 2894 | protect(_revealFocusedElementDeferrer)->fulfill(WebKit::RevealFocusedElementDeferralReason::KeyboardWillShow); |
| 2895 | } |
| 2896 | |
| 2897 | - (void)_keyboardDidShow |
| 2898 | { |
| 2899 | // FIXME: This deferred call to -_zoomToRevealFocusedElement works around the fact that Mail compose |
| 2900 | // disables automatic content inset adjustment using the keyboard height, and instead has logic to |
| 2901 | // explicitly set WKScrollView's contentScrollInset after receiving UIKeyboardDidShowNotification. |
| 2902 | // This means that if we -_zoomToRevealFocusedElement immediately after focusing the body field in |
| 2903 | // Mail, we won't take the keyboard height into account when scrolling. |
| 2904 | // Mitigate this by deferring the call to -_zoomToRevealFocusedElement in this case until after the |
| 2905 | // keyboard has finished animating. We can revert this once rdar://87733414 is fixed. |
| 2906 | RunLoop::mainSingleton().dispatch([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 2907 | auto strongSelf = weakSelf.get(); |
| 2908 | if (!strongSelf || !strongSelf->_revealFocusedElementDeferrer) |
| 2909 | return; |
| 2910 | |
| 2911 | protect(strongSelf->_revealFocusedElementDeferrer)->fulfill(WebKit::RevealFocusedElementDeferralReason::KeyboardDidShow); |
| 2912 | }); |
| 2913 | |
| 2914 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 2915 | [_fileUploadPanel repositionContextMenuIfNeeded:WebKit::KeyboardIsDismissing::No]; |
| 2916 | #endif |
| 2917 | } |
| 2918 | |
| 2919 | - (void)_zoomToRevealFocusedElement |
| 2920 | { |
| 2921 | _revealFocusedElementDeferrer = nullptr; |
| 2922 | |
| 2923 | if (_focusedElementInformation.preventScroll) |
| 2924 | return; |
| 2925 | |
| 2926 | if (_suppressSelectionAssistantReasons || _activeTextInteractionCount) |
| 2927 | return; |
| 2928 | |
| 2929 | // In case user scaling is force enabled, do not use that scaling when zooming in with an input field. |
| 2930 | // Zooming above the page's default scale factor should only happen when the user performs it. |
| 2931 | [self _zoomToFocusRect:_focusedElementInformation.interactionRect |
| 2932 | selectionRect:_didAccessoryTabInitiateFocus ? WebCore::FloatRect() : self.rectToRevealWhenZoomingToFocusedElement |
| 2933 | fontSize:_focusedElementInformation.nodeFontSize |
| 2934 | minimumScale:_focusedElementInformation.minimumScaleFactor |
| 2935 | maximumScale:_focusedElementInformation.maximumScaleFactorIgnoringAlwaysScalable |
| 2936 | allowScaling:_focusedElementInformation.allowsUserScalingIgnoringAlwaysScalable && PAL::currentUserInterfaceIdiomIsSmallScreen() |
| 2937 | forceScroll:[self requiresAccessoryView]]; |
| 2938 | } |
| 2939 | |
| 2940 | - (UIView *)inputView |
| 2941 | { |
| 2942 | return [_webView.get() inputView]; |
| 2943 | } |
| 2944 | |
| 2945 | - (UIView *)inputViewForWebView |
| 2946 | { |
| 2947 | if (!self._hasFocusedElement) |
| 2948 | return nilnullptr; |
| 2949 | |
| 2950 | if (_inputPeripheral) { |
| 2951 | // FIXME: UIKit may invoke -[WKContentView inputView] at any time when WKContentView is the first responder; |
| 2952 | // as such, it doesn't make sense to change the enclosing scroll view's zoom scale and content offset to reveal |
| 2953 | // the focused element here. It seems this behavior was added to match logic in legacy WebKit (refer to |
| 2954 | // UIWebBrowserView). Instead, we should find the places where we currently assume that UIKit (or other clients) |
| 2955 | // invoke -inputView to zoom to the focused element, and either surface SPI for clients to zoom to the focused |
| 2956 | // element, or explicitly trigger the zoom from WebKit. |
| 2957 | // For instance, one use case that currently relies on this detail is adjusting the zoom scale and viewport upon |
| 2958 | // rotation, when a select element is focused. See <https://webkit.org/b/192878> for more information. |
| 2959 | [self _zoomToRevealFocusedElement]; |
| 2960 | |
| 2961 | [self _updateAccessory]; |
| 2962 | } |
| 2963 | |
| 2964 | if (UIView *customInputView = [_formInputSession customInputView]) |
| 2965 | return customInputView; |
| 2966 | |
| 2967 | if (_dataListTextSuggestionsInputView) |
| 2968 | return _dataListTextSuggestionsInputView.get(); |
| 2969 | |
| 2970 | return [_inputPeripheral assistantView]; |
| 2971 | } |
| 2972 | |
| 2973 | - (CGRect)_selectionClipRect |
| 2974 | { |
| 2975 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG162" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(2975, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 2976 | |
| 2977 | return self.selectionClipRect; |
| 2978 | } |
| 2979 | |
| 2980 | static BOOL isBuiltInScrollViewPanGestureRecognizer(UIGestureRecognizer *recognizer) |
| 2981 | { |
| 2982 | static Class scrollViewPanGestureClass = NSClassFromString(@"UIScrollViewPanGestureRecognizer"); |
| 2983 | return [recognizer isKindOfClass:scrollViewPanGestureClass]; |
| 2984 | } |
| 2985 | |
| 2986 | static BOOL isBuiltInScrollViewGestureRecognizer(UIGestureRecognizer *recognizer) |
| 2987 | { |
| 2988 | static Class scrollViewPinchGestureClass = NSClassFromString(@"UIScrollViewPinchGestureRecognizer"); |
| 2989 | static Class scrollViewKnobLongPressGestureRecognizerClass = NSClassFromString(@"UIScrollViewKnobLongPressGestureRecognizer"); |
| 2990 | return isBuiltInScrollViewPanGestureRecognizer(recognizer) |
| 2991 | || [recognizer isKindOfClass:scrollViewPinchGestureClass] |
| 2992 | || [recognizer isKindOfClass:scrollViewKnobLongPressGestureRecognizerClass]; |
| 2993 | } |
| 2994 | |
| 2995 | - (BOOL)_isContextMenuGestureRecognizerForFailureRelationships:(UIGestureRecognizer *)gestureRecognizer |
| 2996 | { |
| 2997 | return [gestureRecognizer.name isEqualToString:@"com.apple.UIKit.clickPresentationFailure"] && gestureRecognizer.view == self; |
| 2998 | } |
| 2999 | |
| 3000 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 3001 | |
| 3002 | - (BOOL)_isDragInitiationGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer |
| 3003 | { |
| 3004 | if (!_dragInteraction) |
| 3005 | return NO__objc_no; |
| 3006 | |
| 3007 | id gestureDelegate = gestureRecognizer.delegate; |
| 3008 | if (![gestureDelegate respondsToSelector:@selector(delegate)]) |
| 3009 | return NO__objc_no; |
| 3010 | |
| 3011 | return _dragInteraction == [gestureDelegate delegate]; |
| 3012 | } |
| 3013 | |
| 3014 | #endif // ENABLE(DRAG_SUPPORT) |
| 3015 | |
| 3016 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer |
| 3017 | { |
| 3018 | // A long-press gesture can not be recognized while panning, but a pan can be recognized |
| 3019 | // during a long-press gesture. |
| 3020 | BOOL shouldNotPreventScrollViewGestures = preventingGestureRecognizer == _highlightLongPressGestureRecognizer || preventingGestureRecognizer == _longPressGestureRecognizer; |
| 3021 | |
| 3022 | return !(shouldNotPreventScrollViewGestures && isBuiltInScrollViewGestureRecognizer(preventedGestureRecognizer)); |
| 3023 | } |
| 3024 | |
| 3025 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer |
| 3026 | { |
| 3027 | if (preventingGestureRecognizer._wk_isTextInteractionLoupeGesture && (preventedGestureRecognizer == _highlightLongPressGestureRecognizer || preventedGestureRecognizer == _longPressGestureRecognizer)) { |
| 3028 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 3029 | return YES__objc_yes; |
| 3030 | #else |
| 3031 | return NO__objc_no; |
| 3032 | #endif |
| 3033 | } |
| 3034 | |
| 3035 | return YES__objc_yes; |
| 3036 | } |
| 3037 | |
| 3038 | - (UIGestureRecognizer *)textInteractionLoupeGestureRecognizer |
| 3039 | { |
| 3040 | if (protect(_cachedTextInteractionLoupeGestureRecognizer).get().view != self) { |
| 3041 | _cachedTextInteractionLoupeGestureRecognizer = nilnullptr; |
| 3042 | for (UIGestureRecognizer *gestureRecognizer in self.gestureRecognizers) { |
| 3043 | if (gestureRecognizer._wk_isTextInteractionLoupeGesture) { |
| 3044 | _cachedTextInteractionLoupeGestureRecognizer = gestureRecognizer; |
| 3045 | break; |
| 3046 | } |
| 3047 | } |
| 3048 | } |
| 3049 | return _cachedTextInteractionLoupeGestureRecognizer; |
| 3050 | } |
| 3051 | |
| 3052 | - (UIGestureRecognizer *)textInteractionTapGestureRecognizer |
| 3053 | { |
| 3054 | if (protect(_cachedTextInteractionTapGestureRecognizer).get().view != self) { |
| 3055 | _cachedTextInteractionTapGestureRecognizer = nilnullptr; |
| 3056 | for (UIGestureRecognizer *gestureRecognizer in self.gestureRecognizers) { |
| 3057 | if (gestureRecognizer._wk_isTextInteractionTapGesture) { |
| 3058 | _cachedTextInteractionTapGestureRecognizer = gestureRecognizer; |
| 3059 | break; |
| 3060 | } |
| 3061 | } |
| 3062 | } |
| 3063 | return _cachedTextInteractionTapGestureRecognizer; |
| 3064 | } |
| 3065 | |
| 3066 | static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UIGestureRecognizer *x, UIGestureRecognizer *y) |
| 3067 | { |
| 3068 | return (a == x && b == y) || (b == x && a == y); |
| 3069 | } |
| 3070 | |
| 3071 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer |
| 3072 | { |
| 3073 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 3074 | if (gestureRecognizer == _gamepadInteractionGestureRecognizer || otherGestureRecognizer == _gamepadInteractionGestureRecognizer) { |
| 3075 | if (WebKit::UIGamepadProvider::singleton().platformWebPageProxyForGamepadInput() == _page) |
| 3076 | return NO__objc_no; |
| 3077 | } |
| 3078 | #endif |
| 3079 | |
| 3080 | if (gestureRecognizer == _keyboardDismissalGestureRecognizer || otherGestureRecognizer == _keyboardDismissalGestureRecognizer) |
| 3081 | return YES__objc_yes; |
| 3082 | |
| 3083 | for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) { |
| 3084 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _touchEventGestureRecognizer.get(), gesture)) |
| 3085 | return YES__objc_yes; |
| 3086 | } |
| 3087 | |
| 3088 | if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class] && [otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class]) |
| 3089 | return YES__objc_yes; |
| 3090 | |
| 3091 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 3092 | if (gestureRecognizer == _imageAnalysisDeferringGestureRecognizer) |
| 3093 | return ![self shouldDeferGestureDueToImageAnalysis:otherGestureRecognizer]; |
| 3094 | |
| 3095 | if (otherGestureRecognizer == _imageAnalysisDeferringGestureRecognizer) |
| 3096 | return ![self shouldDeferGestureDueToImageAnalysis:gestureRecognizer]; |
| 3097 | #endif |
| 3098 | |
| 3099 | if (gestureRecognizer == _singleTapGestureRecognizer |
| 3100 | && isBuiltInScrollViewPanGestureRecognizer(otherGestureRecognizer) |
| 3101 | && [otherGestureRecognizer.view isKindOfClass:UIScrollView.class] |
| 3102 | && ![self _isInterruptingDecelerationForScrollViewOrAncestor:[_singleTapGestureRecognizer lastTouchedScrollView]]) |
| 3103 | return YES__objc_yes; |
| 3104 | |
| 3105 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _longPressGestureRecognizer.get())) |
| 3106 | return YES__objc_yes; |
| 3107 | |
| 3108 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 3109 | if ([_mouseInteraction hasGesture:otherGestureRecognizer]) |
| 3110 | return YES__objc_yes; |
| 3111 | #endif |
| 3112 | |
| 3113 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 3114 | if ((gestureRecognizer == _singleTapGestureRecognizer && otherGestureRecognizer._wk_isTextInteractionLoupeGesture) |
| 3115 | || (otherGestureRecognizer == _singleTapGestureRecognizer && gestureRecognizer._wk_isTextInteractionLoupeGesture)) |
| 3116 | return YES__objc_yes; |
| 3117 | |
| 3118 | if (([gestureRecognizer isKindOfClass:[_UILookupGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) || ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && [gestureRecognizer isKindOfClass:[_UILookupGestureRecognizer class]])) |
| 3119 | return YES__objc_yes; |
| 3120 | #endif // PLATFORM(MACCATALYST) |
| 3121 | |
| 3122 | if (gestureRecognizer == _highlightLongPressGestureRecognizer.get() || otherGestureRecognizer == _highlightLongPressGestureRecognizer.get()) { |
| 3123 | if (gestureRecognizer._wk_isTextInteractionLoupeGesture || otherGestureRecognizer._wk_isTextInteractionLoupeGesture) |
| 3124 | return YES__objc_yes; |
| 3125 | |
| 3126 | if (gestureRecognizer._wk_isTapAndAHalf || otherGestureRecognizer._wk_isTapAndAHalf) |
| 3127 | return YES__objc_yes; |
| 3128 | } |
| 3129 | |
| 3130 | if ((gestureRecognizer == _singleTapGestureRecognizer && otherGestureRecognizer._wk_isTextInteractionTapGesture) |
| 3131 | || (otherGestureRecognizer == _singleTapGestureRecognizer && gestureRecognizer._wk_isTextInteractionTapGesture)) |
| 3132 | return YES__objc_yes; |
| 3133 | |
| 3134 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 3135 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _modelInteractionPanGestureRecognizer.get(), _longPressGestureRecognizer.get())) |
| 3136 | return YES__objc_yes; |
| 3137 | |
| 3138 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _modelInteractionPanGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get())) |
| 3139 | return YES__objc_yes; |
| 3140 | |
| 3141 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _modelInteractionPanGestureRecognizer.get(), _singleTapGestureRecognizer.get())) |
| 3142 | return YES__objc_yes; |
| 3143 | |
| 3144 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _modelInteractionPanGestureRecognizer.get(), _previewGestureRecognizer.get())) |
| 3145 | return YES__objc_yes; |
| 3146 | #endif |
| 3147 | |
| 3148 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _singleTapGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get())) |
| 3149 | return YES__objc_yes; |
| 3150 | |
| 3151 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get())) |
| 3152 | return YES__objc_yes; |
| 3153 | |
| 3154 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewSecondaryGestureRecognizer.get())) |
| 3155 | return YES__objc_yes; |
| 3156 | |
| 3157 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewGestureRecognizer.get())) |
| 3158 | return YES__objc_yes; |
| 3159 | |
| 3160 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _nonBlockingDoubleTapGestureRecognizer.get(), _doubleTapGestureRecognizerForDoubleClick.get())) |
| 3161 | return YES__objc_yes; |
| 3162 | |
| 3163 | if (isSamePair(gestureRecognizer, otherGestureRecognizer, _doubleTapGestureRecognizer.get(), _doubleTapGestureRecognizerForDoubleClick.get())) |
| 3164 | return YES__objc_yes; |
| 3165 | |
| 3166 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 3167 | if (gestureRecognizer == _imageAnalysisGestureRecognizer) |
| 3168 | return YES__objc_yes; |
| 3169 | #endif |
| 3170 | |
| 3171 | #if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION) |
| 3172 | Class graspGesture = NSClassFromString(@"MRUIGraspGestureRecognizer"); |
| 3173 | if (([gestureRecognizer isKindOfClass:graspGesture] && otherGestureRecognizer == _singleTapGestureRecognizer.get()) || (gestureRecognizer == _singleTapGestureRecognizer.get() && [otherGestureRecognizer isKindOfClass:graspGesture])) |
| 3174 | return YES__objc_yes; |
| 3175 | #endif |
| 3176 | |
| 3177 | return NO__objc_no; |
| 3178 | } |
| 3179 | |
| 3180 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer |
| 3181 | { |
| 3182 | if (gestureRecognizer == _touchEventGestureRecognizer && [self _touchEventsMustRequireGestureRecognizerToFail:otherGestureRecognizer]) |
| 3183 | return YES__objc_yes; |
| 3184 | |
| 3185 | if ([otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class]) |
| 3186 | return [(WKDeferringGestureRecognizer *)otherGestureRecognizer shouldDeferGestureRecognizer:gestureRecognizer]; |
| 3187 | |
| 3188 | return NO__objc_no; |
| 3189 | } |
| 3190 | |
| 3191 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer |
| 3192 | { |
| 3193 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 3194 | if (gestureRecognizer == _gamepadInteractionGestureRecognizer || otherGestureRecognizer == _gamepadInteractionGestureRecognizer) { |
| 3195 | if (WebKit::UIGamepadProvider::singleton().platformWebPageProxyForGamepadInput() == _page) |
| 3196 | return NO__objc_no; |
| 3197 | } |
| 3198 | #endif |
| 3199 | |
| 3200 | if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class]) |
| 3201 | return [(WKDeferringGestureRecognizer *)gestureRecognizer shouldDeferGestureRecognizer:otherGestureRecognizer]; |
| 3202 | |
| 3203 | return NO__objc_no; |
| 3204 | } |
| 3205 | |
| 3206 | - (void)_showImageSheet |
| 3207 | { |
| 3208 | [protect(_actionSheetAssistant) showImageSheet]; |
| 3209 | } |
| 3210 | |
| 3211 | - (void)_showAttachmentSheet |
| 3212 | { |
| 3213 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 3214 | if (![uiDelegate respondsToSelector:@selector(_webView:showCustomSheetForElement:)]) |
| 3215 | return; |
| 3216 | |
| 3217 | auto element = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeAttachment image:nilnullptr information:_positionInformation]); |
| 3218 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 3219 | [uiDelegate _webView:self.webView showCustomSheetForElement:element.get()]; |
| 3220 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 3221 | } |
| 3222 | |
| 3223 | - (void)_showLinkSheet |
| 3224 | { |
| 3225 | [protect(_actionSheetAssistant) showLinkSheet]; |
| 3226 | } |
| 3227 | |
| 3228 | - (void)_showDataDetectorsUI |
| 3229 | { |
| 3230 | [self _showDataDetectorsUIForPositionInformation:_positionInformation]; |
| 3231 | } |
| 3232 | |
| 3233 | - (void)_showDataDetectorsUIForPositionInformation:(const WebKit::InteractionInformationAtPosition&)positionInformation |
| 3234 | { |
| 3235 | [protect(_actionSheetAssistant) showDataDetectorsUIForPositionInformation:positionInformation]; |
| 3236 | } |
| 3237 | |
| 3238 | - (SEL)_actionForLongPressFromPositionInformation:(const WebKit::InteractionInformationAtPosition&)positionInformation |
| 3239 | { |
| 3240 | if (!self.webView.configuration._longPressActionsEnabled) |
| 3241 | return nilnullptr; |
| 3242 | |
| 3243 | if (!positionInformation.touchCalloutEnabled) |
| 3244 | return nilnullptr; |
| 3245 | |
| 3246 | if (positionInformation.isImage) |
| 3247 | return @selector(_showImageSheet); |
| 3248 | |
| 3249 | if (positionInformation.isLink) { |
| 3250 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 3251 | if (WebCore::DataDetection::canBePresentedByDataDetectors(positionInformation.url)) |
| 3252 | return @selector(_showDataDetectorsUI); |
| 3253 | #endif |
| 3254 | return @selector(_showLinkSheet); |
| 3255 | } |
| 3256 | if (positionInformation.isAttachment) |
| 3257 | return @selector(_showAttachmentSheet); |
| 3258 | |
| 3259 | return nilnullptr; |
| 3260 | } |
| 3261 | |
| 3262 | - (SEL)_actionForLongPress |
| 3263 | { |
| 3264 | return [self _actionForLongPressFromPositionInformation:_positionInformation]; |
| 3265 | } |
| 3266 | |
| 3267 | - (void)doAfterPositionInformationUpdate:(void (^)(WebKit::InteractionInformationAtPosition))action forRequest:(WebKit::InteractionInformationRequest)request |
| 3268 | { |
| 3269 | if ([self _currentPositionInformationIsValidForRequest:request]) { |
| 3270 | // If the most recent position information is already valid, invoke the given action block immediately. |
| 3271 | action(_positionInformation); |
| 3272 | return; |
| 3273 | } |
| 3274 | |
| 3275 | _pendingPositionInformationHandlers.append(InteractionInformationRequestAndCallback(request, action)); |
| 3276 | |
| 3277 | if (![self _hasValidOutstandingPositionInformationRequest:request]) |
| 3278 | [self requestAsynchronousPositionInformationUpdate:request]; |
| 3279 | } |
| 3280 | |
| 3281 | - (BOOL)ensurePositionInformationIsUpToDate:(WebKit::InteractionInformationRequest)request |
| 3282 | { |
| 3283 | [_webView.get() didEnsurePositionInformationIsUpToDate]; |
| 3284 | if ([self _currentPositionInformationIsValidForRequest:request]) |
| 3285 | return YES__objc_yes; |
| 3286 | |
| 3287 | if (!protect(_page)->hasRunningProcess()) |
| 3288 | return NO__objc_no; |
| 3289 | |
| 3290 | Ref process = _page->legacyMainFrameProcess(); |
| 3291 | if (!process->hasConnection()) |
| 3292 | return NO__objc_no; |
| 3293 | |
| 3294 | if (_isWaitingOnPositionInformation) |
| 3295 | return NO__objc_no; |
| 3296 | |
| 3297 | _isWaitingOnPositionInformation = YES__objc_yes; |
| 3298 | |
| 3299 | if (![self _hasValidOutstandingPositionInformationRequest:request]) |
| 3300 | [self requestAsynchronousPositionInformationUpdate:request]; |
| 3301 | |
| 3302 | bool receivedResponse = protect(process->connection())->waitForAndDispatchImmediately<Messages::WebPageProxy::DidReceivePositionInformation>(_page->webPageIDInMainFrameProcess(), 1_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives) == IPC::Error::NoError; |
| 3303 | _hasValidPositionInformation = receivedResponse && _positionInformation.canBeValid; |
| 3304 | return _hasValidPositionInformation; |
| 3305 | } |
| 3306 | |
| 3307 | - (void)requestAsynchronousPositionInformationUpdate:(WebKit::InteractionInformationRequest)request |
| 3308 | { |
| 3309 | if ([self _currentPositionInformationIsValidForRequest:request]) |
| 3310 | return; |
| 3311 | |
| 3312 | _lastOutstandingPositionInformationRequest = request; |
| 3313 | |
| 3314 | protect(_page)->requestPositionInformation(request); |
| 3315 | } |
| 3316 | |
| 3317 | - (BOOL)_currentPositionInformationIsValidForRequest:(const WebKit::InteractionInformationRequest&)request |
| 3318 | { |
| 3319 | return _hasValidPositionInformation && _positionInformation.request.isValidForRequest(request); |
| 3320 | } |
| 3321 | |
| 3322 | - (BOOL)_hasValidOutstandingPositionInformationRequest:(const WebKit::InteractionInformationRequest&)request |
| 3323 | { |
| 3324 | return _lastOutstandingPositionInformationRequest && _lastOutstandingPositionInformationRequest->isValidForRequest(request); |
| 3325 | } |
| 3326 | |
| 3327 | - (BOOL)_currentPositionInformationIsApproximatelyValidForRequest:(const WebKit::InteractionInformationRequest&)request radiusForApproximation:(int)radius |
| 3328 | { |
| 3329 | return _hasValidPositionInformation && _positionInformation.request.isApproximatelyValidForRequest(request, radius); |
| 3330 | } |
| 3331 | |
| 3332 | - (void)_invokeAndRemovePendingHandlersValidForCurrentPositionInformation |
| 3333 | { |
| 3334 | ASSERT(_hasValidPositionInformation)((void)0); |
| 3335 | |
| 3336 | // FIXME: We need to clean up these handlers in the event that we are not able to collect data, or if the WebProcess crashes. |
| 3337 | ++_positionInformationCallbackDepth; |
| 3338 | auto updatedPositionInformation = _positionInformation; |
| 3339 | |
| 3340 | for (size_t index = 0; index < _pendingPositionInformationHandlers.size(); ++index) { |
| 3341 | auto requestAndHandler = _pendingPositionInformationHandlers[index]; |
| 3342 | if (!requestAndHandler) |
| 3343 | continue; |
| 3344 | |
| 3345 | if (![self _currentPositionInformationIsValidForRequest:requestAndHandler->first]) |
| 3346 | continue; |
| 3347 | |
| 3348 | _pendingPositionInformationHandlers[index] = std::nullopt; |
| 3349 | |
| 3350 | if (requestAndHandler->second) |
| 3351 | requestAndHandler->second(updatedPositionInformation); |
| 3352 | } |
| 3353 | |
| 3354 | if (--_positionInformationCallbackDepth) |
| 3355 | return; |
| 3356 | |
| 3357 | for (int index = _pendingPositionInformationHandlers.size() - 1; index >= 0; --index) { |
| 3358 | if (!_pendingPositionInformationHandlers[index]) |
| 3359 | _pendingPositionInformationHandlers.removeAt(index); |
| 3360 | } |
| 3361 | } |
| 3362 | |
| 3363 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 3364 | - (NSArray *)_dataDetectionResults |
| 3365 | { |
| 3366 | return _page->dataDetectionResults(); |
| 3367 | } |
| 3368 | #endif |
| 3369 | |
| 3370 | - (BOOL)_pointIsInsideSelectionRect:(CGPoint)point outBoundingRect:(WebCore::FloatRect *)outBoundingRect |
| 3371 | { |
| 3372 | BOOL pointIsInSelectionRect = NO__objc_no; |
| 3373 | for (auto& rectInfo : _lastSelectionDrawingInfo.selectionGeometries) { |
| 3374 | auto rect = rectInfo.rect(); |
| 3375 | if (rect.isEmpty()) |
| 3376 | continue; |
| 3377 | |
| 3378 | pointIsInSelectionRect |= rect.contains(WebCore::roundedIntPoint(point)); |
| 3379 | if (outBoundingRect) |
| 3380 | outBoundingRect->unite(rect); |
| 3381 | } |
| 3382 | |
| 3383 | if (!pointIsInSelectionRect) |
| 3384 | return NO__objc_no; |
| 3385 | |
| 3386 | if (!self.selectionHonorsOverflowScrolling) |
| 3387 | return YES__objc_yes; |
| 3388 | |
| 3389 | RetainPtr hitView = [self hitTest:point withEvent:nilnullptr]; |
| 3390 | if (!hitView) |
| 3391 | return NO__objc_no; |
| 3392 | |
| 3393 | RetainPtr hitScroller = dynamic_objc_cast<UIScrollView>(hitView.get()) ?: [hitView _wk_parentScrollView]; |
| 3394 | return hitScroller && hitScroller == self._selectionContainerViewInternal._wk_parentScrollView; |
| 3395 | } |
| 3396 | |
| 3397 | - (BOOL)_shouldToggleEditMenuAfterTapAt:(CGPoint)point |
| 3398 | { |
| 3399 | if (_lastSelectionDrawingInfo.selectionGeometries.isEmpty()) |
| 3400 | return NO__objc_no; |
| 3401 | |
| 3402 | WebCore::FloatRect selectionBoundingRect; |
| 3403 | BOOL pointIsInSelectionRect = [self _pointIsInsideSelectionRect:point outBoundingRect:&selectionBoundingRect]; |
| 3404 | WebCore::FloatRect unobscuredContentRect = self.unobscuredContentRect; |
| 3405 | selectionBoundingRect.intersect(unobscuredContentRect); |
| 3406 | |
| 3407 | float unobscuredArea = unobscuredContentRect.area(); |
| 3408 | float ratioForConsideringSelectionRectToCoverVastMajorityOfContent = 0.75; |
| 3409 | if (!unobscuredArea || selectionBoundingRect.area() / unobscuredArea > ratioForConsideringSelectionRectToCoverVastMajorityOfContent) |
| 3410 | return NO__objc_no; |
| 3411 | |
| 3412 | return pointIsInSelectionRect; |
| 3413 | } |
| 3414 | |
| 3415 | - (BOOL)_hasEnclosingScrollView:(UIView *)firstView matchingCriteria:(Function<BOOL(UIScrollView *)>&&)matchFunction |
| 3416 | { |
| 3417 | RetainPtr view = firstView ?: self.webView.scrollView; |
| 3418 | for (; view; view = view.get().superview) { |
| 3419 | if (RetainPtr scrollView = dynamic_objc_cast<UIScrollView>(view.get()); scrollView && matchFunction(scrollView.get())) |
| 3420 | return YES__objc_yes; |
| 3421 | } |
| 3422 | return NO__objc_no; |
| 3423 | } |
| 3424 | |
| 3425 | - (BOOL)_isPanningScrollViewOrAncestor:(UIScrollView *)scrollView |
| 3426 | { |
| 3427 | return [self _hasEnclosingScrollView:scrollView matchingCriteria:[](UIScrollView *scrollView) { |
| 3428 | if (scrollView.dragging || scrollView.decelerating) |
| 3429 | return YES__objc_yes; |
| 3430 | |
| 3431 | auto *panGesture = scrollView.panGestureRecognizer; |
| 3432 | ASSERT(panGesture)((void)0); |
| 3433 | return [panGesture _wk_hasRecognizedOrEnded]; |
| 3434 | }]; |
| 3435 | } |
| 3436 | |
| 3437 | - (BOOL)_isInterruptingDecelerationForScrollViewOrAncestor:(UIScrollView *)scrollView |
| 3438 | { |
| 3439 | return [self _hasEnclosingScrollView:scrollView matchingCriteria:[](UIScrollView *scrollView) { |
| 3440 | return scrollView._wk_isInterruptingDeceleration; |
| 3441 | }]; |
| 3442 | } |
| 3443 | |
| 3444 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer |
| 3445 | { |
| 3446 | CGPoint point = [gestureRecognizer locationInView:self]; |
| 3447 | |
| 3448 | auto ignoreTapGestureReason = [&](WKScrollViewTrackingTapGestureRecognizer *tapGesture) -> WebKit::IgnoreTapGestureReason { |
| 3449 | if ([self _shouldToggleEditMenuAfterTapAt:point]) |
| 3450 | return WebKit::IgnoreTapGestureReason::ToggleEditMenu; |
| 3451 | |
| 3452 | RetainPtr scrollView = [tapGesture lastTouchedScrollView]; |
| 3453 | if ([self _isPanningScrollViewOrAncestor:scrollView.get()] || [self _isInterruptingDecelerationForScrollViewOrAncestor:scrollView.get()]) |
| 3454 | return WebKit::IgnoreTapGestureReason::DeferToScrollView; |
| 3455 | |
| 3456 | return WebKit::IgnoreTapGestureReason::None; |
| 3457 | }; |
| 3458 | |
| 3459 | if (gestureRecognizer == _singleTapGestureRecognizer) { |
| 3460 | switch (ignoreTapGestureReason(_singleTapGestureRecognizer.get())) { |
| 3461 | case WebKit::IgnoreTapGestureReason::ToggleEditMenu: |
| 3462 | protect(_page)->clearSelectionAfterTappingSelectionHighlightIfNeeded(point); |
| 3463 | [[fallthrough]]; |
| 3464 | case WebKit::IgnoreTapGestureReason::DeferToScrollView: |
| 3465 | return NO__objc_no; |
| 3466 | case WebKit::IgnoreTapGestureReason::None: |
| 3467 | return YES__objc_yes; |
| 3468 | } |
| 3469 | } |
| 3470 | |
| 3471 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 3472 | if (gestureRecognizer == _modelInteractionPanGestureRecognizer) { |
| 3473 | if (!_page->hasModelElement()) |
| 3474 | return NO__objc_no; |
| 3475 | |
| 3476 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3477 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 3478 | return NO__objc_no; |
| 3479 | |
| 3480 | if (self._hasFocusedElement) { |
| 3481 | // Prevent the gesture if it is the same node. |
| 3482 | if (_positionInformation.elementContext && _positionInformation.elementContext->isSameElement(_focusedElementInformation.elementContext)) |
| 3483 | return NO__objc_no; |
| 3484 | } else { |
| 3485 | // Prevent the gesture if there is no action for the node. |
| 3486 | return _positionInformation.isInteractiveModel; |
| 3487 | } |
| 3488 | } |
| 3489 | #endif |
| 3490 | |
| 3491 | if (gestureRecognizer == _keyboardDismissalGestureRecognizer) { |
| 3492 | auto tapIsInEditableRoot = [&] { |
| 3493 | auto& state = protect(_page)->editorState(); |
| 3494 | return state.hasVisualData() && state.visualData->editableRootBounds.contains(WebCore::roundedIntPoint(point)); |
| 3495 | }; |
| 3496 | return self._hasFocusedElement |
| 3497 | && !self.hasHiddenContentEditable |
| 3498 | && !tapIsInEditableRoot() |
| 3499 | && ignoreTapGestureReason(_keyboardDismissalGestureRecognizer.get()) == WebKit::IgnoreTapGestureReason::None; |
| 3500 | } |
| 3501 | |
| 3502 | if (gestureRecognizer == _doubleTapGestureRecognizerForDoubleClick) { |
| 3503 | // Do not start the double-tap-for-double-click gesture recognizer unless we've got a dblclick event handler on the node at the tap location. |
| 3504 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3505 | if (![self _currentPositionInformationIsApproximatelyValidForRequest:request radiusForApproximation:[_doubleTapGestureRecognizerForDoubleClick allowableMovement]]) { |
| 3506 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 3507 | return NO__objc_no; |
| 3508 | } |
| 3509 | return _positionInformation.hitNodeOrWindowHasDoubleClickListener.value_or(false); |
| 3510 | } |
| 3511 | |
| 3512 | if (gestureRecognizer == _highlightLongPressGestureRecognizer |
| 3513 | || gestureRecognizer == _doubleTapGestureRecognizer |
| 3514 | || gestureRecognizer == _nonBlockingDoubleTapGestureRecognizer |
| 3515 | || gestureRecognizer == _twoFingerDoubleTapGestureRecognizer) { |
| 3516 | |
| 3517 | if (self._hasFocusedElement) { |
| 3518 | // Request information about the position with sync message. |
| 3519 | // If the focused element is the same, prevent the gesture. |
| 3520 | if (![self ensurePositionInformationIsUpToDate:WebKit::InteractionInformationRequest(WebCore::roundedIntPoint(point))]) |
| 3521 | return NO__objc_no; |
| 3522 | if (_positionInformation.elementContext && _positionInformation.elementContext->isSameElement(_focusedElementInformation.elementContext)) |
| 3523 | return NO__objc_no; |
| 3524 | } |
| 3525 | } |
| 3526 | |
| 3527 | if (gestureRecognizer == _highlightLongPressGestureRecognizer) { |
| 3528 | if ([self _isInterruptingDecelerationForScrollViewOrAncestor:[_highlightLongPressGestureRecognizer lastTouchedScrollView]]) |
| 3529 | return NO__objc_no; |
| 3530 | |
| 3531 | if (self._hasFocusedElement) { |
| 3532 | // This is a different element than the focused one. |
| 3533 | // Prevent the gesture if there is no node. |
| 3534 | // Allow the gesture if it is a node that wants highlight or if there is an action for it. |
| 3535 | if (!_positionInformation.isElement) |
| 3536 | return NO__objc_no; |
| 3537 | return [self _actionForLongPress] != nilnullptr; |
| 3538 | } |
| 3539 | // We still have no idea about what is at the location. |
| 3540 | // Send an async message to find out. |
| 3541 | _hasValidPositionInformation = NO__objc_no; |
| 3542 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3543 | |
| 3544 | // If 3D Touch is enabled, asynchronously collect snapshots in the hopes that |
| 3545 | // they'll arrive before we have to synchronously request them in |
| 3546 | // _interactionShouldBeginFromPreviewItemController. |
| 3547 | if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { |
| 3548 | request.includeSnapshot = true; |
| 3549 | request.includeLinkIndicator = true; |
| 3550 | request.linkIndicatorShouldHaveLegacyMargins = !self._shouldUseContextMenus; |
| 3551 | request.gatherAnimations = [self.webView _allowAnimationControls]; |
| 3552 | } |
| 3553 | |
| 3554 | [self requestAsynchronousPositionInformationUpdate:request]; |
| 3555 | return YES__objc_yes; |
| 3556 | |
| 3557 | } |
| 3558 | |
| 3559 | if (gestureRecognizer == _longPressGestureRecognizer) { |
| 3560 | // Use the information retrieved with one of the previous calls |
| 3561 | // to gestureRecognizerShouldBegin. |
| 3562 | // Force a sync call if not ready yet. |
| 3563 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3564 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 3565 | return NO__objc_no; |
| 3566 | |
| 3567 | if (self._hasFocusedElement) { |
| 3568 | // Prevent the gesture if it is the same node. |
| 3569 | if (_positionInformation.elementContext && _positionInformation.elementContext->isSameElement(_focusedElementInformation.elementContext)) |
| 3570 | return NO__objc_no; |
| 3571 | } else { |
| 3572 | // Prevent the gesture if there is no action for the node. |
| 3573 | return [self _actionForLongPress] != nilnullptr; |
| 3574 | } |
| 3575 | } |
| 3576 | |
| 3577 | return YES__objc_yes; |
| 3578 | } |
| 3579 | |
| 3580 | - (void)_cancelInteraction |
| 3581 | { |
| 3582 | _isTapHighlightIDValid = NO__objc_no; |
| 3583 | [_tapHighlightView removeFromSuperview]; |
| 3584 | } |
| 3585 | |
| 3586 | - (void)_finishInteraction |
| 3587 | { |
| 3588 | _isTapHighlightIDValid = NO__objc_no; |
| 3589 | [self _fadeTapHighlightViewIfNeeded]; |
| 3590 | } |
| 3591 | |
| 3592 | - (void)_fadeTapHighlightViewIfNeeded |
| 3593 | { |
| 3594 | if (![_tapHighlightView superview] || _isTapHighlightFading) |
| 3595 | return; |
| 3596 | |
| 3597 | _isTapHighlightFading = YES__objc_yes; |
| 3598 | CGFloat tapHighlightFadeDuration = _showDebugTapHighlightsForFastClicking ? 0.25 : 0.1; |
| 3599 | [UIView animateWithDuration:tapHighlightFadeDuration |
| 3600 | animations:^{ |
| 3601 | [_tapHighlightView setAlpha:0]; |
| 3602 | } |
| 3603 | completion:^(BOOL finished) { |
| 3604 | if (finished) |
| 3605 | [_tapHighlightView removeFromSuperview]; |
| 3606 | _isTapHighlightFading = NO__objc_no; |
| 3607 | }]; |
| 3608 | } |
| 3609 | |
| 3610 | - (BOOL)canShowNonEmptySelectionView |
| 3611 | { |
| 3612 | if (_suppressSelectionAssistantReasons) |
| 3613 | return NO__objc_no; |
| 3614 | |
| 3615 | auto& state = protect(_page)->editorState(); |
| 3616 | return state.hasPostLayoutData() && state.selectionType != WebCore::SelectionType::None; |
| 3617 | } |
| 3618 | |
| 3619 | - (BOOL)hasSelectablePositionAtPoint:(CGPoint)point |
| 3620 | { |
| 3621 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 3622 | if ([_imageAnalysisInteraction interactableItemExistsAtPoint:point]) |
| 3623 | return NO__objc_no; |
| 3624 | #endif |
| 3625 | |
| 3626 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 3627 | if (!self.webView.configuration._textInteractionGesturesEnabled) |
| 3628 | return NO__objc_no; |
| 3629 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 3630 | |
| 3631 | if (!protect(_page->preferences())->textInteractionEnabled()) |
| 3632 | return NO__objc_no; |
| 3633 | |
| 3634 | if (_suppressSelectionAssistantReasons) |
| 3635 | return NO__objc_no; |
| 3636 | |
| 3637 | if (_inspectorNodeSearchEnabled) |
| 3638 | return NO__objc_no; |
| 3639 | |
| 3640 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3641 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 3642 | return NO__objc_no; |
| 3643 | |
| 3644 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 3645 | if (_elementPendingImageAnalysis && _positionInformation.hostImageOrVideoElementContext == _elementPendingImageAnalysis) |
| 3646 | return YES__objc_yes; |
| 3647 | #endif |
| 3648 | |
| 3649 | return _positionInformation.isSelectable(); |
| 3650 | } |
| 3651 | |
| 3652 | - (BOOL)pointIsNearMarkedText:(CGPoint)point |
| 3653 | { |
| 3654 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 3655 | if ([_imageAnalysisInteraction interactableItemExistsAtPoint:point]) |
| 3656 | return NO__objc_no; |
| 3657 | #endif |
| 3658 | |
| 3659 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 3660 | if (!self.webView.configuration._textInteractionGesturesEnabled) |
| 3661 | return NO__objc_no; |
| 3662 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 3663 | |
| 3664 | if (!protect(_page->preferences())->textInteractionEnabled()) |
| 3665 | return NO__objc_no; |
| 3666 | |
| 3667 | if (_suppressSelectionAssistantReasons) |
| 3668 | return NO__objc_no; |
| 3669 | |
| 3670 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3671 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 3672 | return NO__objc_no; |
| 3673 | return _positionInformation.isNearMarkedText; |
| 3674 | } |
| 3675 | |
| 3676 | - (BOOL)textInteractionGesture:(WKBEGestureTypeBEGestureType)gesture shouldBeginAtPoint:(CGPoint)point |
| 3677 | { |
| 3678 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 3679 | if (gesture == WKBEGestureTypeForceTouchBEGestureTypeForceTouch) |
| 3680 | return [self hasSelectablePositionAtPoint:point]; |
| 3681 | #endif |
| 3682 | |
| 3683 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 3684 | if ([_imageAnalysisInteraction interactableItemExistsAtPoint:point]) |
| 3685 | return NO__objc_no; |
| 3686 | #endif |
| 3687 | |
| 3688 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 3689 | if (!self.webView.configuration._textInteractionGesturesEnabled) |
| 3690 | return NO__objc_no; |
| 3691 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 3692 | |
| 3693 | if (!protect(_page->preferences())->textInteractionEnabled()) |
| 3694 | return NO__objc_no; |
| 3695 | |
| 3696 | if (_domPasteRequestHandler) |
| 3697 | return NO__objc_no; |
| 3698 | |
| 3699 | if (_suppressSelectionAssistantReasons) |
| 3700 | return NO__objc_no; |
| 3701 | |
| 3702 | if (!self.isFocusingElement) { |
| 3703 | if (gesture == WKBEGestureTypeDoubleTapBEGestureTypeDoubleTap) { |
| 3704 | // Don't allow double tap text gestures in noneditable content. |
| 3705 | return NO__objc_no; |
| 3706 | } |
| 3707 | |
| 3708 | if (gesture == WKBEGestureTypeOneFingerTapBEGestureTypeOneFingerTap) { |
| 3709 | ASSERT(_suppressNonEditableSingleTapTextInteractionCount >= 0)((void)0); |
| 3710 | if (_suppressNonEditableSingleTapTextInteractionCount > 0) |
| 3711 | return NO__objc_no; |
| 3712 | |
| 3713 | if (self.textInteractionLoupeGestureRecognizer._wk_hasRecognizedOrEnded) { |
| 3714 | // Avoid handling one-finger taps while the web process is processing certain selection changes. |
| 3715 | // This works around a scenario where UIKeyboardImpl blocks the main thread while handling a one- |
| 3716 | // finger tap, which subsequently prevents the UI process from handling any incoming IPC messages. |
| 3717 | return NO__objc_no; |
| 3718 | } |
| 3719 | return protect(_page)->editorState().selectionType == WebCore::SelectionType::Range; |
| 3720 | } |
| 3721 | } |
| 3722 | |
| 3723 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 3724 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 3725 | return NO__objc_no; |
| 3726 | |
| 3727 | if (gesture == WKBEGestureTypeLoupeBEGestureTypeLoupe && _positionInformation.selectability == WebKit::InteractionInformationAtPosition::Selectability::UnselectableDueToUserSelectNoneOrQuirk) |
| 3728 | return NO__objc_no; |
| 3729 | |
| 3730 | if (_positionInformation.preventTextInteraction) |
| 3731 | return NO__objc_no; |
| 3732 | |
| 3733 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 3734 | if (_elementPendingImageAnalysis && _positionInformation.hostImageOrVideoElementContext == _elementPendingImageAnalysis) |
| 3735 | return YES__objc_yes; |
| 3736 | #endif |
| 3737 | |
| 3738 | // If we're currently focusing an editable element, only allow the selection to move within that focused element. |
| 3739 | if (self.isFocusingElement) |
| 3740 | return _positionInformation.elementContext && _positionInformation.elementContext->isSameElement(_focusedElementInformation.elementContext); |
| 3741 | |
| 3742 | if (_positionInformation.prefersDraggingOverTextSelection) |
| 3743 | return NO__objc_no; |
| 3744 | |
| 3745 | // If we're selecting something, don't activate highlight. |
| 3746 | if (gesture == WKBEGestureTypeLoupeBEGestureTypeLoupe && [self hasSelectablePositionAtPoint:point]) |
| 3747 | [self _cancelLongPressGestureRecognizer]; |
| 3748 | |
| 3749 | // Otherwise, if we're using a text interaction assistant outside of editing purposes (e.g. the selection mode |
| 3750 | // is character granularity) then allow text selection. |
| 3751 | return YES__objc_yes; |
| 3752 | } |
| 3753 | |
| 3754 | - (NSArray *)webSelectionRectsForSelectionGeometries:(const Vector<WebCore::SelectionGeometry>&)selectionGeometries |
| 3755 | { |
| 3756 | if (selectionGeometries.isEmpty()) |
| 3757 | return nilnullptr; |
| 3758 | |
| 3759 | return createNSArray(selectionGeometries, [] (auto& geometry) { |
| 3760 | auto webRect = [WebSelectionRect selectionRect]; |
| 3761 | webRect.rect = geometry.rect(); |
| 3762 | webRect.writingDirection = geometry.direction() == WebCore::TextDirection::LTR ? WKWritingDirectionLeftToRight : WKWritingDirectionRightToLeft; |
| 3763 | webRect.isLineBreak = geometry.isLineBreak(); |
| 3764 | webRect.isFirstOnLine = geometry.isFirstOnLine(); |
| 3765 | webRect.isLastOnLine = geometry.isLastOnLine(); |
| 3766 | webRect.containsStart = geometry.containsStart(); |
| 3767 | webRect.containsEnd = geometry.containsEnd(); |
| 3768 | webRect.isInFixedPosition = geometry.isInFixedPosition(); |
| 3769 | webRect.isHorizontal = geometry.isHorizontal(); |
| 3770 | return webRect; |
| 3771 | }).autorelease(); |
| 3772 | } |
| 3773 | |
| 3774 | - (NSArray *)webSelectionRects |
| 3775 | { |
| 3776 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG163" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(3776, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 3777 | |
| 3778 | if (!protect(_page)->editorState().hasPostLayoutAndVisualData() || protect(_page)->editorState().selectionType == WebCore::SelectionType::None) |
| 3779 | return nilnullptr; |
| 3780 | const auto& selectionGeometries = protect(_page)->editorState().visualData->selectionGeometries; |
| 3781 | return [self webSelectionRectsForSelectionGeometries:selectionGeometries]; |
| 3782 | } |
| 3783 | |
| 3784 | - (WebKit::TapIdentifier)nextTapIdentifier |
| 3785 | { |
| 3786 | _latestTapID = WebKit::TapIdentifier::generate(); |
| 3787 | return *_latestTapID; |
| 3788 | } |
| 3789 | |
| 3790 | - (void)_highlightLongPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer |
| 3791 | { |
| 3792 | ASSERT(gestureRecognizer == _highlightLongPressGestureRecognizer)((void)0); |
| 3793 | [self _resetIsDoubleTapPending]; |
| 3794 | |
| 3795 | auto startPoint = [gestureRecognizer startPoint]; |
| 3796 | |
| 3797 | _lastInteractionLocation = startPoint; |
| 3798 | |
| 3799 | switch ([gestureRecognizer state]) { |
| 3800 | case UIGestureRecognizerStateBegan: |
| 3801 | _longPressCanClick = YES__objc_yes; |
| 3802 | cancelPotentialTapIfNecessary(self); |
| 3803 | protect(_page)->tapHighlightAtPosition(startPoint, [self nextTapIdentifier]); |
| 3804 | _isTapHighlightIDValid = YES__objc_yes; |
| 3805 | break; |
| 3806 | case UIGestureRecognizerStateEnded: |
| 3807 | if (_longPressCanClick && _positionInformation.isElement) { |
| 3808 | [self _attemptSyntheticClickAtLocation:startPoint modifierFlags:gestureRecognizer.modifierFlags]; |
| 3809 | [self _finishInteraction]; |
| 3810 | } else |
| 3811 | [self _cancelInteraction]; |
| 3812 | _longPressCanClick = NO__objc_no; |
| 3813 | break; |
| 3814 | case UIGestureRecognizerStateCancelled: |
| 3815 | [self _cancelInteraction]; |
| 3816 | _longPressCanClick = NO__objc_no; |
| 3817 | break; |
| 3818 | default: |
| 3819 | break; |
| 3820 | } |
| 3821 | } |
| 3822 | |
| 3823 | - (void)_doubleTapRecognizedForDoubleClick:(UITapGestureRecognizer *)gestureRecognizer |
| 3824 | { |
| 3825 | RELEASE_ASSERT(_layerTreeTransactionIdAtLastInteractionStart)do { if (__builtin_expect(!!(!(_layerTreeTransactionIdAtLastInteractionStart )), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(3825, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 3826 | protect(_page)->handleDoubleTapForDoubleClickAtPoint(WebCore::IntPoint([gestureRecognizer locationInView:self]), WebKit::webEventModifierFlags(gestureRecognizer.modifierFlags), *_layerTreeTransactionIdAtLastInteractionStart); |
| 3827 | } |
| 3828 | |
| 3829 | - (void)_twoFingerSingleTapGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 3830 | { |
| 3831 | _isTapHighlightIDValid = YES__objc_yes; |
| 3832 | _isExpectingFastSingleTapCommit = YES__objc_yes; |
| 3833 | protect(_page)->handleTwoFingerTapAtPoint(WebCore::roundedIntPoint([gestureRecognizer locationInView:self]), WebKit::webEventModifierFlags(gestureRecognizer.modifierFlags | UIKeyModifierCommand), [self nextTapIdentifier]); |
| 3834 | } |
| 3835 | |
| 3836 | - (void)_longPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer |
| 3837 | { |
| 3838 | ASSERT(gestureRecognizer == _longPressGestureRecognizer)((void)0); |
| 3839 | [self _resetIsDoubleTapPending]; |
| 3840 | [self _cancelTouchEventGestureRecognizer]; |
| 3841 | |
| 3842 | protect(_page)->didRecognizeLongPress(); |
| 3843 | |
| 3844 | _lastInteractionLocation = [gestureRecognizer startPoint]; |
| 3845 | |
| 3846 | if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) { |
| 3847 | SEL action = [self _actionForLongPress]; |
| 3848 | if (action) { |
| 3849 | [self performSelector:action]; |
| 3850 | [self _cancelLongPressGestureRecognizer]; |
| 3851 | } |
| 3852 | } |
| 3853 | } |
| 3854 | |
| 3855 | - (void)_endPotentialTapAndEnableDoubleTapGesturesIfNecessary |
| 3856 | { |
| 3857 | if (self.webView._allowsDoubleTapGestures) { |
| 3858 | RELEASE_LOG(ViewGestures, "ending potential tap - double taps are back. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("ending potential tap - double taps are back. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG164" ) = "ending potential tap - double taps are back. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("ending potential tap - double taps are back. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "ending potential tap - double taps are back. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 3859 | |
| 3860 | [self _setDoubleTapGesturesEnabled:YES__objc_yes]; |
| 3861 | } |
| 3862 | |
| 3863 | RELEASE_LOG(ViewGestures, "Ending potential tap. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Ending potential tap. (%p, pageProxyID=%llu)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG165" ) = "Ending potential tap. (%p, pageProxyID=%llu)"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Ending potential tap. (%p, pageProxyID=%llu)", self, _page-> identifier().toUInt64())]; _os_log_impl(&__dso_handle, _log_tmp , _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format( _os_fmt_buf, "Ending potential tap. (%p, pageProxyID=%llu)", self , _page->identifier().toUInt64()), (uint32_t)sizeof(_os_fmt_buf )) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 3864 | |
| 3865 | _potentialTapInProgress = NO__objc_no; |
| 3866 | } |
| 3867 | |
| 3868 | - (BOOL)isPotentialTapInProgress |
| 3869 | { |
| 3870 | return _potentialTapInProgress; |
| 3871 | } |
| 3872 | |
| 3873 | - (void)_singleTapIdentified:(UITapGestureRecognizer *)gestureRecognizer |
| 3874 | { |
| 3875 | auto position = [gestureRecognizer locationInView:self]; |
| 3876 | |
| 3877 | if ([self _handleTapOverInteractiveControl:position]) |
| 3878 | return; |
| 3879 | |
| 3880 | ASSERT(gestureRecognizer == _singleTapGestureRecognizer)((void)0); |
| 3881 | ASSERT(!_potentialTapInProgress)((void)0); |
| 3882 | [self _resetIsDoubleTapPending]; |
| 3883 | |
| 3884 | [_inputPeripheral setSingleTapShouldEndEditing:[_inputPeripheral isEditing]]; |
| 3885 | |
| 3886 | bool shouldRequestMagnificationInformation = protect(_page->preferences())->fasterClicksEnabled(); |
| 3887 | if (shouldRequestMagnificationInformation) |
| 3888 | RELEASE_LOG(ViewGestures, "Single tap identified. Request details on potential zoom. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Single tap identified. Request details on potential zoom. (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG166" ) = "Single tap identified. Request details on potential zoom. (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Single tap identified. Request details on potential zoom. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Single tap identified. Request details on potential zoom. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 3889 | |
| 3890 | protect(_page)->potentialTapAtPosition(std::nullopt, position, shouldRequestMagnificationInformation, [self nextTapIdentifier], WebKit::WebMouseEventInputSource::UserDriven); |
| 3891 | _potentialTapInProgress = YES__objc_yes; |
| 3892 | _isTapHighlightIDValid = YES__objc_yes; |
| 3893 | _isExpectingFastSingleTapCommit = !_doubleTapGestureRecognizer.get().enabled; |
| 3894 | } |
| 3895 | |
| 3896 | static void cancelPotentialTapIfNecessary(WKContentView* contentView) |
| 3897 | { |
| 3898 | if (contentView->_potentialTapInProgress) { |
| 3899 | [contentView _endPotentialTapAndEnableDoubleTapGesturesIfNecessary]; |
| 3900 | [contentView _cancelInteraction]; |
| 3901 | protect(contentView->_page)->cancelPotentialTap(); |
| 3902 | } |
| 3903 | } |
| 3904 | |
| 3905 | - (void)_singleTapDidReset:(UITapGestureRecognizer *)gestureRecognizer |
| 3906 | { |
| 3907 | ASSERT(gestureRecognizer == _singleTapGestureRecognizer)((void)0); |
| 3908 | cancelPotentialTapIfNecessary(self); |
| 3909 | if (auto* singleTapTouchIdentifier = [_singleTapGestureRecognizer lastActiveTouchIdentifier]) { |
| 3910 | WebCore::PointerID pointerId = [singleTapTouchIdentifier unsignedIntValue]; |
| 3911 | if (_commitPotentialTapPointerId != pointerId) |
| 3912 | protect(_page)->touchWithIdentifierWasRemoved(pointerId); |
| 3913 | } |
| 3914 | |
| 3915 | if (!_isTapHighlightIDValid) |
| 3916 | [self _fadeTapHighlightViewIfNeeded]; |
| 3917 | } |
| 3918 | |
| 3919 | - (void)_keyboardDismissalGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 3920 | { |
| 3921 | ASSERT(gestureRecognizer == _keyboardDismissalGestureRecognizer)((void)0); |
| 3922 | |
| 3923 | if (!self._hasFocusedElement) |
| 3924 | return; |
| 3925 | |
| 3926 | protect(_page)->shouldDismissKeyboardAfterTapAtPoint([gestureRecognizer locationInView:self], [weakSelf = WeakObjCPtr<WKContentView>(self), element = _focusedElementInformation.elementContext](bool shouldDismiss) { |
| 3927 | if (!shouldDismiss) |
| 3928 | return; |
| 3929 | |
| 3930 | RetainPtr strongSelf = weakSelf.get(); |
| 3931 | if (![strongSelf _isSameAsFocusedElement:element]) |
| 3932 | return; |
| 3933 | |
| 3934 | RELEASE_LOG(ViewGestures, "Dismissing keyboard after tap (%p, pageProxyID=%llu)", strongSelf.get(), strongSelf->_page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Dismissing keyboard after tap (%p, pageProxyID=%llu)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG167" ) = "Dismissing keyboard after tap (%p, pageProxyID=%llu)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Dismissing keyboard after tap (%p, pageProxyID=%llu)", strongSelf .get(), strongSelf->_page->identifier().toUInt64())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Dismissing keyboard after tap (%p, pageProxyID=%llu)" , strongSelf.get(), strongSelf->_page->identifier().toUInt64 ()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }) ; } }) clang diagnostic pop ; |
| 3935 | [strongSelf _elementDidBlur]; |
| 3936 | }); |
| 3937 | } |
| 3938 | |
| 3939 | - (void)_doubleTapDidFail:(UITapGestureRecognizer *)gestureRecognizer |
| 3940 | { |
| 3941 | RELEASE_LOG(ViewGestures, "Double tap was not recognized. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Double tap was not recognized. (%p, pageProxyID=%llu)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG168" ) = "Double tap was not recognized. (%p, pageProxyID=%llu)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Double tap was not recognized. (%p, pageProxyID=%llu)", self , _page->identifier().toUInt64())]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Double tap was not recognized. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 3942 | ASSERT(gestureRecognizer == _doubleTapGestureRecognizer)((void)0); |
| 3943 | } |
| 3944 | |
| 3945 | - (void)_commitPotentialTapFailed |
| 3946 | { |
| 3947 | protect(_page)->touchWithIdentifierWasRemoved(_commitPotentialTapPointerId); |
| 3948 | _commitPotentialTapPointerId = 0; |
| 3949 | |
| 3950 | [self _cancelInteraction]; |
| 3951 | |
| 3952 | [self stopDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::TapGesture]; |
| 3953 | } |
| 3954 | |
| 3955 | - (void)_didNotHandleTapAsClick:(const WebCore::IntPoint&)point |
| 3956 | { |
| 3957 | [self stopDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::TapGesture]; |
| 3958 | |
| 3959 | if (!_isDoubleTapPending) |
| 3960 | return; |
| 3961 | |
| 3962 | protect(_smartMagnificationController)->handleSmartMagnificationGesture(_lastInteractionLocation); |
| 3963 | _isDoubleTapPending = NO__objc_no; |
| 3964 | } |
| 3965 | |
| 3966 | - (void)_didHandleTapAsHover |
| 3967 | { |
| 3968 | [self stopDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::TapGesture]; |
| 3969 | } |
| 3970 | |
| 3971 | - (void)_didCompleteSyntheticClick |
| 3972 | { |
| 3973 | protect(_page)->touchWithIdentifierWasRemoved(_commitPotentialTapPointerId); |
| 3974 | _commitPotentialTapPointerId = 0; |
| 3975 | |
| 3976 | RELEASE_LOG(ViewGestures, "Synthetic click completed. (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Synthetic click completed. (%p, pageProxyID=%llu)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG169" ) = "Synthetic click completed. (%p, pageProxyID=%llu)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Synthetic click completed. (%p, pageProxyID=%llu)", self, _page ->identifier().toUInt64())]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Synthetic click completed. (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 3977 | [self stopDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::TapGesture]; |
| 3978 | } |
| 3979 | |
| 3980 | #if ENABLE(GAMEPAD)(defined 1 && 1) |
| 3981 | - (void)_gamepadInteractionGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 3982 | { |
| 3983 | // This space intentionally left blank. |
| 3984 | } |
| 3985 | #endif |
| 3986 | |
| 3987 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 3988 | - (void)_modelInteractionPanGestureRecognized:(UIPanGestureRecognizer *)gestureRecognizer |
| 3989 | { |
| 3990 | ASSERT(gestureRecognizer == _modelInteractionPanGestureRecognizer)((void)0); |
| 3991 | |
| 3992 | _lastInteractionLocation = [gestureRecognizer locationInView:self]; |
| 3993 | |
| 3994 | switch (gestureRecognizer.state) { |
| 3995 | case UIGestureRecognizerStateBegan: { |
| 3996 | [self modelInteractionPanGestureDidBeginAtPoint:_lastInteractionLocation]; |
| 3997 | break; |
| 3998 | } |
| 3999 | case UIGestureRecognizerStateChanged: { |
| 4000 | [self modelInteractionPanGestureDidUpdateWithPoint:_lastInteractionLocation]; |
| 4001 | break; |
| 4002 | } |
| 4003 | case UIGestureRecognizerStateEnded: |
| 4004 | case UIGestureRecognizerStateCancelled: { |
| 4005 | [self modelInteractionPanGestureDidEnd]; |
| 4006 | break; |
| 4007 | } |
| 4008 | default: |
| 4009 | break; |
| 4010 | } |
| 4011 | } |
| 4012 | #endif |
| 4013 | |
| 4014 | - (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 4015 | { |
| 4016 | ASSERT(gestureRecognizer == _singleTapGestureRecognizer)((void)0); |
| 4017 | |
| 4018 | if (!_potentialTapInProgress) |
| 4019 | return; |
| 4020 | |
| 4021 | if (![self isFirstResponder]) { |
| 4022 | [self startDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::TapGesture]; |
| 4023 | [self becomeFirstResponder]; |
| 4024 | } |
| 4025 | |
| 4026 | _lastInteractionLocation = [gestureRecognizer locationInView:self]; |
| 4027 | |
| 4028 | [self _endPotentialTapAndEnableDoubleTapGesturesIfNecessary]; |
| 4029 | |
| 4030 | if (_hasTapHighlightForPotentialTap) { |
| 4031 | [self _showTapHighlight]; |
| 4032 | _hasTapHighlightForPotentialTap = NO__objc_no; |
| 4033 | } |
| 4034 | |
| 4035 | if ([_inputPeripheral singleTapShouldEndEditing]) |
| 4036 | [_inputPeripheral endEditing]; |
| 4037 | |
| 4038 | RELEASE_LOG(ViewGestures, "Single tap recognized - commit potential tap (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Single tap recognized - commit potential tap (%p, pageProxyID=%llu)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG170" ) = "Single tap recognized - commit potential tap (%p, pageProxyID=%llu)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Single tap recognized - commit potential tap (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64())]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Single tap recognized - commit potential tap (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 4039 | |
| 4040 | WebCore::PointerID pointerId = WebCore::mousePointerID; |
| 4041 | if (auto* singleTapTouchIdentifier = [_singleTapGestureRecognizer lastActiveTouchIdentifier]) { |
| 4042 | pointerId = [singleTapTouchIdentifier unsignedIntValue]; |
| 4043 | _commitPotentialTapPointerId = pointerId; |
| 4044 | } |
| 4045 | RELEASE_ASSERT(_layerTreeTransactionIdAtLastInteractionStart)do { if (__builtin_expect(!!(!(_layerTreeTransactionIdAtLastInteractionStart )), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4045, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 4046 | protect(_page)->commitPotentialTap(std::nullopt, WebKit::webEventModifierFlags(gestureRecognizer.modifierFlags), *_layerTreeTransactionIdAtLastInteractionStart, pointerId); |
| 4047 | |
| 4048 | if (!_isExpectingFastSingleTapCommit) |
| 4049 | [self _finishInteraction]; |
| 4050 | |
| 4051 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 4052 | if (![_imageAnalysisInteraction interactableItemExistsAtPoint:_lastInteractionLocation]) |
| 4053 | [_imageAnalysisInteraction resetSelection]; |
| 4054 | #endif |
| 4055 | } |
| 4056 | |
| 4057 | - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 4058 | { |
| 4059 | RELEASE_LOG(ViewGestures, "Identified a double tap (%p, pageProxyID=%llu)", self, _page->identifier().toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewGestures .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Identified a double tap (%p, pageProxyID=%llu)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG171" ) = "Identified a double tap (%p, pageProxyID=%llu)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Identified a double tap (%p, pageProxyID=%llu)", self, _page ->identifier().toUInt64())]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Identified a double tap (%p, pageProxyID=%llu)" , self, _page->identifier().toUInt64()), (uint32_t)sizeof( _os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 4060 | |
| 4061 | [self _resetIsDoubleTapPending]; |
| 4062 | |
| 4063 | auto location = [gestureRecognizer locationInView:self]; |
| 4064 | _lastInteractionLocation = location; |
| 4065 | protect(_smartMagnificationController)->handleSmartMagnificationGesture(location); |
| 4066 | } |
| 4067 | |
| 4068 | - (void)_resetIsDoubleTapPending |
| 4069 | { |
| 4070 | _isDoubleTapPending = NO__objc_no; |
| 4071 | } |
| 4072 | |
| 4073 | - (void)_nonBlockingDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 4074 | { |
| 4075 | _lastInteractionLocation = [gestureRecognizer locationInView:self]; |
| 4076 | _isDoubleTapPending = YES__objc_yes; |
| 4077 | } |
| 4078 | |
| 4079 | - (void)_twoFingerDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer |
| 4080 | { |
| 4081 | [self _resetIsDoubleTapPending]; |
| 4082 | |
| 4083 | auto location = [gestureRecognizer locationInView:self]; |
| 4084 | _lastInteractionLocation = location; |
| 4085 | protect(_smartMagnificationController)->handleResetMagnificationGesture(location); |
| 4086 | } |
| 4087 | |
| 4088 | - (void)_attemptSyntheticClickAtLocation:(CGPoint)location modifierFlags:(UIKeyModifierFlags)modifierFlags |
| 4089 | { |
| 4090 | if (![self isFirstResponder]) |
| 4091 | [self becomeFirstResponder]; |
| 4092 | |
| 4093 | [_inputPeripheral endEditing]; |
| 4094 | RELEASE_ASSERT(_layerTreeTransactionIdAtLastInteractionStart)do { if (__builtin_expect(!!(!(_layerTreeTransactionIdAtLastInteractionStart )), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4094, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 4095 | protect(_page)->attemptSyntheticClick(location, WebKit::webEventModifierFlags(modifierFlags), *_layerTreeTransactionIdAtLastInteractionStart); |
| 4096 | } |
| 4097 | |
| 4098 | - (void)setUpTextSelectionAssistant |
| 4099 | { |
| 4100 | if (!_textInteractionWrapper) |
| 4101 | _textInteractionWrapper = adoptNS([[WKTextInteractionWrapper alloc] initWithView:self]); |
| 4102 | else { |
| 4103 | // Reset the gesture recognizers in case editability has changed. |
| 4104 | [_textInteractionWrapper setGestureRecognizers]; |
| 4105 | } |
| 4106 | |
| 4107 | _cachedTextInteractionLoupeGestureRecognizer = nilnullptr; |
| 4108 | _cachedTextInteractionTapGestureRecognizer = nilnullptr; |
| 4109 | } |
| 4110 | |
| 4111 | - (void)pasteWithCompletionHandler:(void (^)(void))completionHandler |
| 4112 | { |
| 4113 | protect(_page)->executeEditCommand("Paste"_s, { }, [completion = makeBlockPtr(completionHandler)] { |
| 4114 | if (completion) |
| 4115 | completion(); |
| 4116 | }); |
| 4117 | } |
| 4118 | |
| 4119 | - (void)clearSelection |
| 4120 | { |
| 4121 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG172" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4121, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4122 | |
| 4123 | [self _internalClearSelection]; |
| 4124 | } |
| 4125 | |
| 4126 | - (void)_internalClearSelection |
| 4127 | { |
| 4128 | [self _elementDidBlur]; |
| 4129 | protect(_page)->clearSelection(); |
| 4130 | } |
| 4131 | |
| 4132 | - (void)_invalidateCurrentPositionInformation |
| 4133 | { |
| 4134 | _hasValidPositionInformation = NO__objc_no; |
| 4135 | _positionInformation = { }; |
| 4136 | } |
| 4137 | |
| 4138 | - (void)_positionInformationDidChange:(const WebKit::InteractionInformationAtPosition&)info |
| 4139 | { |
| 4140 | if (_lastOutstandingPositionInformationRequest && info.request.isValidForRequest(*_lastOutstandingPositionInformationRequest)) |
| 4141 | _lastOutstandingPositionInformationRequest = std::nullopt; |
| 4142 | |
| 4143 | _isWaitingOnPositionInformation = NO__objc_no; |
| 4144 | |
| 4145 | WebKit::InteractionInformationAtPosition newInfo = info; |
| 4146 | newInfo.mergeCompatibleOptionalInformation(_positionInformation); |
| 4147 | |
| 4148 | _positionInformation = newInfo; |
| 4149 | _hasValidPositionInformation = _positionInformation.canBeValid; |
| 4150 | if (_actionSheetAssistant) |
| 4151 | [protect(_actionSheetAssistant) updateSheetPosition]; |
| 4152 | [self _invokeAndRemovePendingHandlersValidForCurrentPositionInformation]; |
| 4153 | } |
| 4154 | |
| 4155 | - (void)_willStartScrollingOrZooming |
| 4156 | { |
| 4157 | [_textInteractionWrapper willStartScrollingOrZooming]; |
| 4158 | protect(_page)->setIsScrollingOrZooming(true); |
| 4159 | |
| 4160 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 4161 | [protect(_focusedFormControlView) disengageFocusedFormControlNavigation]; |
| 4162 | #endif |
| 4163 | } |
| 4164 | |
| 4165 | - (void)scrollViewWillStartPanOrPinchGesture |
| 4166 | { |
| 4167 | protect(_page)->hideValidationMessage(); |
| 4168 | |
| 4169 | [protect(_keyboardScrollingAnimator) willStartInteractiveScroll]; |
| 4170 | |
| 4171 | _touchEventsCanPreventNativeGestures = NO__objc_no; |
| 4172 | } |
| 4173 | |
| 4174 | - (void)_didEndScrollingOrZooming |
| 4175 | { |
| 4176 | if (!_needsDeferredEndScrollingSelectionUpdate) |
| 4177 | [_textInteractionWrapper didEndScrollingOrZooming]; |
| 4178 | protect(_page)->setIsScrollingOrZooming(false); |
| 4179 | |
| 4180 | [self _resetPanningPreventionFlags]; |
| 4181 | |
| 4182 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 4183 | [protect(_focusedFormControlView) engageFocusedFormControlNavigation]; |
| 4184 | #endif |
| 4185 | } |
| 4186 | |
| 4187 | - (BOOL)_elementTypeRequiresAccessoryView:(WebKit::InputType)type |
| 4188 | { |
| 4189 | switch (type) { |
| 4190 | case WebKit::InputType::None: |
| 4191 | case WebKit::InputType::Color: |
| 4192 | case WebKit::InputType::Drawing: |
| 4193 | case WebKit::InputType::Date: |
| 4194 | case WebKit::InputType::DateTimeLocal: |
| 4195 | case WebKit::InputType::Month: |
| 4196 | case WebKit::InputType::Time: |
| 4197 | #if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 4198 | case WebKit::InputType::Week: |
| 4199 | #endif |
| 4200 | return NO__objc_no; |
| 4201 | case WebKit::InputType::Select: { |
| 4202 | if (self._shouldUseContextMenusForFormControls) |
| 4203 | return NO__objc_no; |
| 4204 | return PAL::currentUserInterfaceIdiomIsSmallScreen(); |
| 4205 | } |
| 4206 | case WebKit::InputType::Text: |
| 4207 | case WebKit::InputType::Password: |
| 4208 | case WebKit::InputType::Search: |
| 4209 | case WebKit::InputType::Email: |
| 4210 | case WebKit::InputType::URL: |
| 4211 | case WebKit::InputType::Phone: |
| 4212 | case WebKit::InputType::Number: |
| 4213 | case WebKit::InputType::NumberPad: |
| 4214 | case WebKit::InputType::ContentEditable: |
| 4215 | case WebKit::InputType::TextArea: |
| 4216 | #if !ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 4217 | case WebKit::InputType::Week: |
| 4218 | #endif |
| 4219 | return PAL::currentUserInterfaceIdiomIsSmallScreen(); |
| 4220 | } |
| 4221 | } |
| 4222 | |
| 4223 | - (BOOL)requiresAccessoryView |
| 4224 | { |
| 4225 | #if ENABLE(WRITING_TOOLS)(defined 1 && 1) |
| 4226 | if (_isPresentingWritingTools) |
| 4227 | return NO__objc_no; |
| 4228 | #endif |
| 4229 | |
| 4230 | if ([_formInputSession accessoryViewShouldNotShow]) |
| 4231 | return NO__objc_no; |
| 4232 | |
| 4233 | if ([_formInputSession customInputAccessoryView]) |
| 4234 | return YES__objc_yes; |
| 4235 | |
| 4236 | if ([_webView.get() _isDisplayingPDF]) |
| 4237 | return NO__objc_no; |
| 4238 | |
| 4239 | return [self _elementTypeRequiresAccessoryView:_focusedElementInformation.elementType]; |
| 4240 | } |
| 4241 | |
| 4242 | - (UITextInputAssistantItem *)inputAssistantItem |
| 4243 | { |
| 4244 | return [_webView.get() inputAssistantItem]; |
| 4245 | } |
| 4246 | |
| 4247 | - (UITextInputAssistantItem *)inputAssistantItemForWebView |
| 4248 | { |
| 4249 | return [super inputAssistantItem]; |
| 4250 | } |
| 4251 | |
| 4252 | - (UIView *)inputAccessoryView |
| 4253 | { |
| 4254 | return [_webView.get() inputAccessoryView]; |
| 4255 | } |
| 4256 | |
| 4257 | - (UIView *)inputAccessoryViewForWebView |
| 4258 | { |
| 4259 | if (![self requiresAccessoryView]) |
| 4260 | return nilnullptr; |
| 4261 | |
| 4262 | return [_formInputSession customInputAccessoryView] ?: self.formAccessoryView; |
| 4263 | } |
| 4264 | |
| 4265 | - (NSArray *)supportedPasteboardTypesForCurrentSelection |
| 4266 | { |
| 4267 | if (protect(_page)->editorState().selectionType == WebCore::SelectionType::None) |
| 4268 | return nilnullptr; |
| 4269 | |
| 4270 | if (protect(_page)->editorState().isContentRichlyEditable) |
| 4271 | return WebKit::supportedRichTextPasteboardTypes(); |
| 4272 | |
| 4273 | return WebKit::supportedPlainTextPasteboardTypes(); |
| 4274 | } |
| 4275 | |
| 4276 | #define FORWARD_ACTION_TO_WKWEBVIEW(_action) \ |
| 4277 | - (void)_action:(id)sender \ |
| 4278 | { \ |
| 4279 | SEL action = @selector(_action:);\ |
| 4280 | [self _willPerformAction:action sender:sender];\ |
| 4281 | [_webView.get() _action:sender]; \ |
| 4282 | [self _didPerformAction:action sender:sender];\ |
| 4283 | } |
| 4284 | |
| 4285 | FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKWEBVIEW)FORWARD_ACTION_TO_WKWEBVIEW(captureTextFromCamera) FORWARD_ACTION_TO_WKWEBVIEW (useSelectionForFind) FORWARD_ACTION_TO_WKWEBVIEW(findSelected ) FORWARD_ACTION_TO_WKWEBVIEW(_findSelected) FORWARD_ACTION_TO_WKWEBVIEW (addShortcut) FORWARD_ACTION_TO_WKWEBVIEW(_addShortcut) FORWARD_ACTION_TO_WKWEBVIEW (define) FORWARD_ACTION_TO_WKWEBVIEW(_define) FORWARD_ACTION_TO_WKWEBVIEW (lookup) FORWARD_ACTION_TO_WKWEBVIEW(_lookup) FORWARD_ACTION_TO_WKWEBVIEW (translate) FORWARD_ACTION_TO_WKWEBVIEW(_translate) FORWARD_ACTION_TO_WKWEBVIEW (promptForReplace) FORWARD_ACTION_TO_WKWEBVIEW(_promptForReplace ) FORWARD_ACTION_TO_WKWEBVIEW(share) FORWARD_ACTION_TO_WKWEBVIEW (_share) FORWARD_ACTION_TO_WKWEBVIEW(transliterateChinese) FORWARD_ACTION_TO_WKWEBVIEW (_transliterateChinese) FORWARD_ACTION_TO_WKWEBVIEW(_nextAccessoryTab ) FORWARD_ACTION_TO_WKWEBVIEW(_previousAccessoryTab) FORWARD_ACTION_TO_WKWEBVIEW (copy) FORWARD_ACTION_TO_WKWEBVIEW(cut) FORWARD_ACTION_TO_WKWEBVIEW (paste) FORWARD_ACTION_TO_WKWEBVIEW(replace) FORWARD_ACTION_TO_WKWEBVIEW (select) FORWARD_ACTION_TO_WKWEBVIEW(selectAll) FORWARD_ACTION_TO_WKWEBVIEW (toggleBoldface) FORWARD_ACTION_TO_WKWEBVIEW(toggleItalics) FORWARD_ACTION_TO_WKWEBVIEW (toggleUnderline) FORWARD_ACTION_TO_WKWEBVIEW(increaseSize) FORWARD_ACTION_TO_WKWEBVIEW (decreaseSize) FORWARD_ACTION_TO_WKWEBVIEW(pasteAndMatchStyle ) FORWARD_ACTION_TO_WKWEBVIEW(makeTextWritingDirectionNatural ) FORWARD_ACTION_TO_WKWEBVIEW(makeTextWritingDirectionLeftToRight ) FORWARD_ACTION_TO_WKWEBVIEW(makeTextWritingDirectionRightToLeft ) FORWARD_ACTION_TO_WKWEBVIEW(alignCenter) FORWARD_ACTION_TO_WKWEBVIEW (alignJustified) FORWARD_ACTION_TO_WKWEBVIEW(alignLeft) FORWARD_ACTION_TO_WKWEBVIEW (alignRight) |
The return value is +1 and results in a memory leak | |
| 4286 | FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKWEBVIEW)FORWARD_ACTION_TO_WKWEBVIEW(_indent) FORWARD_ACTION_TO_WKWEBVIEW (_outdent) FORWARD_ACTION_TO_WKWEBVIEW(_toggleStrikeThrough) FORWARD_ACTION_TO_WKWEBVIEW (_insertOrderedList) FORWARD_ACTION_TO_WKWEBVIEW(_insertUnorderedList ) FORWARD_ACTION_TO_WKWEBVIEW(_insertNestedOrderedList) FORWARD_ACTION_TO_WKWEBVIEW (_insertNestedUnorderedList) FORWARD_ACTION_TO_WKWEBVIEW(_increaseListLevel ) FORWARD_ACTION_TO_WKWEBVIEW(_decreaseListLevel) FORWARD_ACTION_TO_WKWEBVIEW (_changeListType) FORWARD_ACTION_TO_WKWEBVIEW(_pasteAsQuotation ) FORWARD_ACTION_TO_WKWEBVIEW(_pasteAndMatchStyle) |
| 4287 | |
| 4288 | #undef FORWARD_ACTION_TO_WKWEBVIEW |
| 4289 | |
| 4290 | - (void)_lookupForWebView:(id)sender |
| 4291 | { |
| 4292 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG173" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4292, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4293 | |
| 4294 | [self lookupForWebView:sender]; |
| 4295 | } |
| 4296 | |
| 4297 | - (void)defineForWebView:(id)sender |
| 4298 | { |
| 4299 | [self lookupForWebView:sender]; |
| 4300 | } |
| 4301 | |
| 4302 | - (void)lookupForWebView:(id)sender |
| 4303 | { |
| 4304 | protect(_page)->getSelectionContext([view = retainPtr(self)](const String& selectedText, const String& textBefore, const String& textAfter) { |
| 4305 | if (!selectedText) |
| 4306 | return; |
| 4307 | |
| 4308 | Ref page = *view->_page; |
| 4309 | auto& editorState = page->editorState(); |
| 4310 | if (!editorState.hasPostLayoutAndVisualData()) |
| 4311 | return; |
| 4312 | auto& visualData = *editorState.visualData; |
| 4313 | CGRect presentationRect; |
| 4314 | if (editorState.selectionType == WebCore::SelectionType::Range && !visualData.selectionGeometries.isEmpty()) |
| 4315 | presentationRect = page->selectionBoundingRectInRootViewCoordinates(); |
| 4316 | else |
| 4317 | presentationRect = visualData.caretRectAtStart; |
| 4318 | |
| 4319 | auto selectionContext = makeString(textBefore, selectedText, textAfter); |
| 4320 | NSRange selectedRangeInContext = NSMakeRange(textBefore.length(), selectedText.length()); |
| 4321 | |
| 4322 | if (auto textSelectionAssistant = view->_textInteractionWrapper) |
| 4323 | [textSelectionAssistant lookup:selectionContext.createNSString().get() withRange:selectedRangeInContext fromRect:presentationRect]; |
| 4324 | }); |
| 4325 | } |
| 4326 | |
| 4327 | - (void)_shareForWebView:(id)sender |
| 4328 | { |
| 4329 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG174" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4329, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4330 | |
| 4331 | [self shareForWebView:sender]; |
| 4332 | } |
| 4333 | |
| 4334 | - (void)shareForWebView:(id)sender |
| 4335 | { |
| 4336 | RetainPtr<WKContentView> view = self; |
| 4337 | protect(_page)->getSelectionOrContentsAsString([view](const String& string) { |
| 4338 | if (!view->_textInteractionWrapper || !string) |
| 4339 | return; |
| 4340 | |
| 4341 | Ref page = *view->_page; |
| 4342 | if (!page->editorState().hasVisualData()) |
| 4343 | return; |
| 4344 | |
| 4345 | auto& selectionGeometries = page->editorState().visualData->selectionGeometries; |
| 4346 | if (selectionGeometries.isEmpty()) |
| 4347 | return; |
| 4348 | |
| 4349 | [view->_textInteractionWrapper showShareSheetFor:string.createNSString().get() fromRect:selectionGeometries.first().rect()]; |
| 4350 | }); |
| 4351 | } |
| 4352 | |
| 4353 | - (void)_translateForWebView:(id)sender |
| 4354 | { |
| 4355 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG175" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4355, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4356 | |
| 4357 | [self translateForWebView:sender]; |
| 4358 | } |
| 4359 | |
| 4360 | - (void)translateForWebView:(id)sender |
| 4361 | { |
| 4362 | protect(_page)->getSelectionOrContentsAsString([weakSelf = WeakObjCPtr<WKContentView>(self)] (const String& string) { |
| 4363 | if (!weakSelf) |
| 4364 | return; |
| 4365 | |
| 4366 | if (string.isEmpty()) |
| 4367 | return; |
| 4368 | |
| 4369 | auto strongSelf = weakSelf.get(); |
| 4370 | Ref page = *strongSelf->_page; |
| 4371 | if (!page->editorState().hasVisualData()) |
| 4372 | return; |
| 4373 | |
| 4374 | if (page->editorState().visualData->selectionGeometries.isEmpty()) |
| 4375 | return; |
| 4376 | |
| 4377 | [strongSelf->_textInteractionWrapper translate:string.createNSString().get() fromRect:page->selectionBoundingRectInRootViewCoordinates()]; |
| 4378 | }); |
| 4379 | } |
| 4380 | |
| 4381 | - (void)_addShortcutForWebView:(id)sender |
| 4382 | { |
| 4383 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG176" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4383, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4384 | |
| 4385 | [self addShortcutForWebView:sender]; |
| 4386 | } |
| 4387 | |
| 4388 | - (void)addShortcutForWebView:(id)sender |
| 4389 | { |
| 4390 | Ref page = *_page; |
| 4391 | if (!page->editorState().visualData) |
| 4392 | return; |
| 4393 | [_textInteractionWrapper showTextServiceFor:[self selectedText] fromRect:page->editorState().visualData->selectionGeometries[0].rect()]; |
| 4394 | } |
| 4395 | |
| 4396 | - (NSString *)selectedText |
| 4397 | { |
| 4398 | Ref page = *_page; |
| 4399 | if (!page->editorState().postLayoutData) |
| 4400 | return nilnullptr; |
| 4401 | return page->editorState().postLayoutData->wordAtSelection.createNSString().autorelease(); |
| 4402 | } |
| 4403 | |
| 4404 | - (NSArray *)alternativesForSelectedText |
| 4405 | { |
| 4406 | Ref page = *_page; |
| 4407 | if (!page->editorState().postLayoutData) |
| 4408 | return nilnullptr; |
| 4409 | auto& dictationContextsForSelection = page->editorState().postLayoutData->dictationContextsForSelection; |
| 4410 | return createNSArray(dictationContextsForSelection, [&] (auto& dictationContext) -> NSObject * { |
| 4411 | RetainPtr alternatives = protect(page->pageClient())->platformDictationAlternatives(dictationContext); |
| 4412 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 4413 | if (!self.shouldUseAsyncInteractions) |
| 4414 | return [[alternatives _nsTextAlternative] autorelease]; |
| 4415 | #endif |
| 4416 | return alternatives.autorelease(); |
| 4417 | }).autorelease(); |
| 4418 | } |
| 4419 | |
| 4420 | - (void)makeTextWritingDirectionNaturalForWebView:(id)sender |
| 4421 | { |
| 4422 | // Match platform behavior on iOS as well as legacy WebKit behavior by modifying the |
| 4423 | // base (paragraph) writing direction rather than the inline direction. |
| 4424 | protect(_page)->setBaseWritingDirection(WebCore::WritingDirection::Natural); |
| 4425 | } |
| 4426 | |
| 4427 | - (void)makeTextWritingDirectionLeftToRightForWebView:(id)sender |
| 4428 | { |
| 4429 | protect(_page)->setBaseWritingDirection(WebCore::WritingDirection::LeftToRight); |
| 4430 | } |
| 4431 | |
| 4432 | - (void)makeTextWritingDirectionRightToLeftForWebView:(id)sender |
| 4433 | { |
| 4434 | protect(_page)->setBaseWritingDirection(WebCore::WritingDirection::RightToLeft); |
| 4435 | } |
| 4436 | |
| 4437 | - (BOOL)isReplaceAllowed |
| 4438 | { |
| 4439 | Ref page = *_page; |
| 4440 | if (!page->editorState().postLayoutData) |
| 4441 | return NO__objc_no; |
| 4442 | return page->editorState().postLayoutData->isReplaceAllowed; |
| 4443 | } |
| 4444 | |
| 4445 | - (void)replaceText:(NSString *)text withText:(NSString *)word |
| 4446 | { |
| 4447 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 4448 | protect(_page)->replaceSelectedText(text, word); |
| 4449 | } |
| 4450 | |
| 4451 | - (void)_promptForReplaceForWebView:(id)sender |
| 4452 | { |
| 4453 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG177" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4453, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4454 | |
| 4455 | [self promptForReplaceForWebView:sender]; |
| 4456 | } |
| 4457 | |
| 4458 | - (void)promptForReplaceForWebView:(id)sender |
| 4459 | { |
| 4460 | Ref page = *_page; |
| 4461 | if (!page->editorState().postLayoutData) |
| 4462 | return; |
| 4463 | const auto& wordAtSelection = page->editorState().postLayoutData->wordAtSelection; |
| 4464 | if (wordAtSelection.isEmpty()) |
| 4465 | return; |
| 4466 | |
| 4467 | [_textInteractionWrapper scheduleReplacementsForText:wordAtSelection.createNSString().get()]; |
| 4468 | } |
| 4469 | |
| 4470 | - (void)_transliterateChineseForWebView:(id)sender |
| 4471 | { |
| 4472 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG178" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(4472, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 4473 | |
| 4474 | [self transliterateChineseForWebView:sender]; |
| 4475 | } |
| 4476 | |
| 4477 | - (void)transliterateChineseForWebView:(id)sender |
| 4478 | { |
| 4479 | Ref page = *_page; |
| 4480 | if (!page->editorState().postLayoutData) |
| 4481 | return; |
| 4482 | [_textInteractionWrapper scheduleChineseTransliterationForText:page->editorState().postLayoutData->wordAtSelection.createNSString().get()]; |
| 4483 | } |
| 4484 | |
| 4485 | - (void)replaceForWebView:(id)sender |
| 4486 | { |
| 4487 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 4488 | if (self.shouldUseAsyncInteractions) { |
| 4489 | [protect(_asyncInputDelegate) textInput:self.asBETextInput deferReplaceTextActionToSystem:sender]; |
| 4490 | return; |
| 4491 | } |
| 4492 | #endif |
| 4493 | [[UIKeyboardImpl sharedInstance] replaceText:sender]; |
| 4494 | } |
| 4495 | |
| 4496 | #define WEBCORE_COMMAND_FOR_WEBVIEW(command) \ |
| 4497 | - (void)_ ## command ## ForWebView:(id)sender { protect(_page)->executeEditCommand(#command ## _s); } \ |
| 4498 | - (void)command ## ForWebView:(id)sender { [self _ ## command ## ForWebView:sender]; } |
| 4499 | WEBCORE_COMMAND_FOR_WEBVIEW(insertOrderedList); |
| 4500 | WEBCORE_COMMAND_FOR_WEBVIEW(insertUnorderedList); |
| 4501 | WEBCORE_COMMAND_FOR_WEBVIEW(insertNestedOrderedList); |
| 4502 | WEBCORE_COMMAND_FOR_WEBVIEW(insertNestedUnorderedList); |
| 4503 | WEBCORE_COMMAND_FOR_WEBVIEW(indent); |
| 4504 | WEBCORE_COMMAND_FOR_WEBVIEW(outdent); |
| 4505 | WEBCORE_COMMAND_FOR_WEBVIEW(alignLeft); |
| 4506 | WEBCORE_COMMAND_FOR_WEBVIEW(alignRight); |
| 4507 | WEBCORE_COMMAND_FOR_WEBVIEW(alignCenter); |
| 4508 | WEBCORE_COMMAND_FOR_WEBVIEW(alignJustified); |
| 4509 | WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle); |
| 4510 | #undef WEBCORE_COMMAND_FOR_WEBVIEW |
| 4511 | |
| 4512 | - (void)_increaseListLevelForWebView:(id)sender |
| 4513 | { |
| 4514 | protect(_page)->increaseListLevel(); |
| 4515 | } |
| 4516 | |
| 4517 | - (void)_decreaseListLevelForWebView:(id)sender |
| 4518 | { |
| 4519 | protect(_page)->decreaseListLevel(); |
| 4520 | } |
| 4521 | |
| 4522 | - (void)_changeListTypeForWebView:(id)sender |
| 4523 | { |
| 4524 | protect(_page)->changeListType(); |
| 4525 | } |
| 4526 | |
| 4527 | - (void)_toggleStrikeThroughForWebView:(id)sender |
| 4528 | { |
| 4529 | protect(_page)->executeEditCommand("StrikeThrough"_s); |
| 4530 | } |
| 4531 | |
| 4532 | - (void)increaseSizeForWebView:(id)sender |
| 4533 | { |
| 4534 | protect(_page)->executeEditCommand("FontSizeDelta"_s, "1"_s); |
| 4535 | } |
| 4536 | |
| 4537 | - (void)decreaseSizeForWebView:(id)sender |
| 4538 | { |
| 4539 | protect(_page)->executeEditCommand("FontSizeDelta"_s, "-1"_s); |
| 4540 | } |
| 4541 | |
| 4542 | - (void)_setFontForWebView:(UIFont *)font sender:(id)sender |
| 4543 | { |
| 4544 | WebCore::FontChanges changes; |
| 4545 | changes.setFontFamily(font.familyName); |
| 4546 | changes.setFontName(font.fontName); |
| 4547 | changes.setFontSize(font.pointSize); |
| 4548 | changes.setBold(font.traits & UIFontTraitBold); |
| 4549 | changes.setItalic(font.traits & UIFontTraitItalic); |
| 4550 | |
| 4551 | if (NSString *textStyleAttribute = [font.fontDescriptor.fontAttributes objectForKey:UIFontDescriptorTextStyleAttribute]) |
| 4552 | changes.setFontFamily(textStyleAttribute); |
| 4553 | |
| 4554 | protect(_page)->changeFont(WTF::move(changes)); |
| 4555 | } |
| 4556 | |
| 4557 | - (void)_setFontSizeForWebView:(CGFloat)fontSize sender:(id)sender |
| 4558 | { |
| 4559 | WebCore::FontChanges changes; |
| 4560 | changes.setFontSize(fontSize); |
| 4561 | protect(_page)->changeFont(WTF::move(changes)); |
| 4562 | } |
| 4563 | |
| 4564 | - (void)_setTextColorForWebView:(UIColor *)color sender:(id)sender |
| 4565 | { |
| 4566 | WebCore::Color textColor(WebCore::roundAndClampToSRGBALossy(protect(protect(color).get().CGColor))); |
| 4567 | protect(_page)->executeEditCommand("ForeColor"_s, WebCore::serializationForHTML(textColor)); |
| 4568 | } |
| 4569 | |
| 4570 | - (void)toggleStrikeThroughForWebView:(id)sender |
| 4571 | { |
| 4572 | [self _toggleStrikeThroughForWebView:sender]; |
| 4573 | } |
| 4574 | |
| 4575 | - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction |
| 4576 | { |
| 4577 | NSMutableDictionary* result = [NSMutableDictionary dictionary]; |
| 4578 | [result setObject:[UIColor blackColor] forKey:NSForegroundColorAttributeNamegetNSForegroundColorAttributeNameSingleton()]; |
| 4579 | if (!position) |
| 4580 | return result; |
| 4581 | |
| 4582 | Ref page = *_page; |
| 4583 | if (!page->editorState().isContentRichlyEditable) |
| 4584 | return result; |
| 4585 | |
| 4586 | if (!page->editorState().postLayoutData) |
| 4587 | return result; |
| 4588 | |
| 4589 | auto typingAttributes = page->editorState().postLayoutData->typingAttributes; |
| 4590 | |
| 4591 | RetainPtr font = _autocorrectionData.font.get(); |
| 4592 | double zoomScale = self._contentZoomScale; |
| 4593 | if (std::abs(zoomScale - 1) > FLT_EPSILON1.19209290e-7F) |
| 4594 | font = [font fontWithSize:[font pointSize] * zoomScale]; |
| 4595 | |
| 4596 | if (font) { |
| 4597 | auto originalTraits = [font fontDescriptor].symbolicTraits; |
| 4598 | auto newTraits = originalTraits; |
| 4599 | if (typingAttributes.contains(WebKit::TypingAttribute::Bold)) |
| 4600 | newTraits |= UIFontDescriptorTraitBold; |
| 4601 | |
| 4602 | if (typingAttributes.contains(WebKit::TypingAttribute::Italics)) |
| 4603 | newTraits |= UIFontDescriptorTraitItalic; |
| 4604 | |
| 4605 | if (originalTraits != newTraits) { |
| 4606 | RetainPtr descriptor = [protect([font fontDescriptor] ?: adoptNS([UIFontDescriptor new]).get()) fontDescriptorWithSymbolicTraits:newTraits]; |
| 4607 | if (RetainPtr fontWithTraits = [UIFont fontWithDescriptor:descriptor.get() size:[font pointSize]]) |
| 4608 | font = WTF::move(fontWithTraits); |
| 4609 | } |
| 4610 | [result setObject:font.get() forKey:NSFontAttributeNamegetNSFontAttributeNameSingleton()]; |
| 4611 | } |
| 4612 | |
| 4613 | if (typingAttributes.contains(WebKit::TypingAttribute::Underline)) |
| 4614 | [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeNamegetNSUnderlineStyleAttributeNameSingleton()]; |
| 4615 | |
| 4616 | return result; |
| 4617 | } |
| 4618 | |
| 4619 | - (UIColor *)insertionPointColor |
| 4620 | { |
| 4621 | // On macCatalyst we need to explicitly return the color we have calculated, rather than rely on textTraits, as on macCatalyst, UIKit ignores text traits. |
| 4622 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 4623 | return [self _cascadeInteractionTintColor]; |
| 4624 | #else |
| 4625 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 4626 | if (!self._requiresLegacyTextInputTraits) |
| 4627 | return self.extendedTraitsDelegate.insertionPointColor; |
| 4628 | #endif |
| 4629 | return [self.textInputTraits insertionPointColor]; |
| 4630 | #endif |
| 4631 | } |
| 4632 | |
| 4633 | - (UIColor *)selectionBarColor |
| 4634 | { |
| 4635 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 4636 | if (!self._requiresLegacyTextInputTraits) |
| 4637 | return self.extendedTextInputTraits.selectionHandleColor; |
| 4638 | #endif |
| 4639 | return [self.textInputTraits selectionBarColor]; |
| 4640 | } |
| 4641 | |
| 4642 | - (UIColor *)selectionHighlightColor |
| 4643 | { |
| 4644 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 4645 | if (!self._requiresLegacyTextInputTraits) |
| 4646 | return self.extendedTraitsDelegate.selectionHighlightColor; |
| 4647 | #endif |
| 4648 | return [self.textInputTraits selectionHighlightColor]; |
| 4649 | } |
| 4650 | |
| 4651 | - (BOOL)_hasCustomTintColor |
| 4652 | { |
| 4653 | if (!_cachedHasCustomTintColor) { |
| 4654 | auto defaultTintColor = WebCore::colorFromCocoaColor([adoptNS([UIView new]) tintColor]); |
| 4655 | _cachedHasCustomTintColor = defaultTintColor != WebCore::colorFromCocoaColor(self.tintColor); |
| 4656 | } |
| 4657 | return *_cachedHasCustomTintColor; |
| 4658 | } |
| 4659 | |
| 4660 | - (UIColor *)_cascadeInteractionTintColor |
| 4661 | { |
| 4662 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 4663 | if (!self.webView.configuration._textInteractionGesturesEnabled) |
| 4664 | return [UIColor clearColor]; |
| 4665 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 4666 | |
| 4667 | if (!protect(_page->preferences())->textInteractionEnabled()) |
| 4668 | return [UIColor clearColor]; |
| 4669 | |
| 4670 | RetainPtr<UIColor> caretColorFromStyle; |
| 4671 | BOOL hasExplicitlySetCaretColorFromStyle = NO__objc_no; |
| 4672 | if (protect(_page)->editorState().hasPostLayoutData()) { |
| 4673 | auto& postLayoutData = *protect(_page)->editorState().postLayoutData; |
| 4674 | if (auto caretColor = postLayoutData.caretColor; caretColor.isValid()) { |
| 4675 | caretColorFromStyle = cocoaColor(caretColor); |
| 4676 | hasExplicitlySetCaretColorFromStyle = !postLayoutData.hasCaretColorAuto; |
| 4677 | } |
| 4678 | } |
| 4679 | |
| 4680 | if (hasExplicitlySetCaretColorFromStyle) |
| 4681 | return caretColorFromStyle.autorelease(); |
| 4682 | |
| 4683 | auto tintColorFromNativeView = self.tintColor; |
| 4684 | if (self._hasCustomTintColor) |
| 4685 | return tintColorFromNativeView; |
| 4686 | |
| 4687 | return caretColorFromStyle.autorelease() ?: tintColorFromNativeView; |
| 4688 | } |
| 4689 | |
| 4690 | - (void)_updateTextInputTraitsForInteractionTintColor |
| 4691 | { |
| 4692 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 4693 | auto tintColor = [self _cascadeInteractionTintColor]; |
| 4694 | [_legacyTextInputTraits _setColorsToMatchTintColor:tintColor]; |
| 4695 | [_extendedTextInputTraits setSelectionColorsToMatchTintColor:tintColor]; |
| 4696 | #endif |
| 4697 | } |
| 4698 | |
| 4699 | - (void)tintColorDidChange |
| 4700 | { |
| 4701 | [super tintColorDidChange]; |
| 4702 | |
| 4703 | _cachedHasCustomTintColor = std::nullopt; |
| 4704 | |
| 4705 | BOOL shouldUpdateTextSelection = self.isFirstResponder && [self canShowNonEmptySelectionView]; |
| 4706 | if (shouldUpdateTextSelection) |
| 4707 | [_textInteractionWrapper deactivateSelection]; |
| 4708 | [self _updateTextInputTraitsForInteractionTintColor]; |
| 4709 | if (shouldUpdateTextSelection) |
| 4710 | [_textInteractionWrapper activateSelection]; |
| 4711 | |
| 4712 | protect(_page)->insertionPointColorDidChange(); |
| 4713 | } |
| 4714 | |
| 4715 | - (BOOL)shouldAllowHighlightLinkCreation |
| 4716 | { |
| 4717 | Ref page = *_page; |
| 4718 | URL url { page->currentURL() }; |
| 4719 | if (!url.isValid() || !url.protocolIsInHTTPFamily() || [_webView.get() _isDisplayingPDF]) |
| 4720 | return NO__objc_no; |
| 4721 | |
| 4722 | auto editorState = page->editorState(); |
| 4723 | return editorState.selectionType == WebCore::SelectionType::Range && !editorState.isContentEditable && !editorState.selectionIsRangeInsideImageOverlay; |
| 4724 | } |
| 4725 | |
| 4726 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender |
| 4727 | { |
| 4728 | if (_domPasteRequestHandler) |
| 4729 | return action == @selector(paste:); |
| 4730 | |
| 4731 | auto& editorState = protect(_page)->editorState(); |
| 4732 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 4733 | if (self.shouldUseAsyncInteractions) { |
| 4734 | if (action == @selector(moveInLayoutDirection:) || action == @selector(extendInLayoutDirection:) || action == @selector(moveInStorageDirection:byGranularity:) || action == @selector(extendInStorageDirection:byGranularity:)) |
| 4735 | return editorState.selectionType != WebCore::SelectionType::None; |
| 4736 | |
| 4737 | if (action == @selector(deleteInDirection:toGranularity:) || action == @selector(transposeCharactersAroundSelection)) |
| 4738 | return editorState.isContentEditable; |
| 4739 | } else |
| 4740 | #endif // USE(BROWSERENGINEKIT) |
| 4741 | { |
| 4742 | // These are UIKit IPI selectors. We don't want to forward them to the web view. |
| 4743 | if (action == @selector(_moveDown:withHistory:) || action == @selector(_moveLeft:withHistory:) || action == @selector(_moveRight:withHistory:) |
| 4744 | || action == @selector(_moveToEndOfDocument:withHistory:) || action == @selector(_moveToEndOfLine:withHistory:) || action == @selector(_moveToEndOfParagraph:withHistory:) |
| 4745 | || action == @selector(_moveToEndOfWord:withHistory:) || action == @selector(_moveToStartOfDocument:withHistory:) || action == @selector(_moveToStartOfLine:withHistory:) |
| 4746 | || action == @selector(_moveToStartOfParagraph:withHistory:) || action == @selector(_moveToStartOfWord:withHistory:) || action == @selector(_moveUp:withHistory:)) |
| 4747 | return editorState.selectionType != WebCore::SelectionType::None; |
| 4748 | |
| 4749 | if (action == @selector(_deleteByWord) || action == @selector(_deleteForwardByWord) || action == @selector(_deleteForwardAndNotify:) |
| 4750 | || action == @selector(_deleteToEndOfParagraph) || action == @selector(_deleteToStartOfLine) || action == @selector(_transpose)) |
| 4751 | return editorState.isContentEditable; |
| 4752 | } |
| 4753 | |
| 4754 | return [_webView.get() canPerformAction:action withSender:sender]; |
| 4755 | } |
| 4756 | |
| 4757 | - (BOOL)canPerformActionForWebView:(SEL)action withSender:(id)sender |
| 4758 | { |
| 4759 | if (_domPasteRequestHandler) |
| 4760 | return action == @selector(paste:); |
| 4761 | |
| 4762 | if (action == @selector(_nextAccessoryTab:)) |
| 4763 | return self._hasFocusedElement && _focusedElementInformation.hasNextNode; |
| 4764 | if (action == @selector(_previousAccessoryTab:)) |
| 4765 | return self._hasFocusedElement && _focusedElementInformation.hasPreviousNode; |
| 4766 | |
| 4767 | auto editorState = protect(_page)->editorState(); |
| 4768 | // FIXME: Some of the following checks should be removed once internal clients move to the underscore-prefixed versions. |
| 4769 | if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:) || action == @selector(_toggleStrikeThrough:) |
| 4770 | || action == @selector(_alignLeft:) || action == @selector(_alignRight:) || action == @selector(_alignCenter:) || action == @selector(_alignJustified:) |
| 4771 | || action == @selector(alignLeft:) || action == @selector(alignRight:) || action == @selector(alignCenter:) || action == @selector(alignJustified:) |
| 4772 | || action == @selector(_setTextColor:sender:) || action == @selector(_setFont:sender:) || action == @selector(_setFontSize:sender:) |
| 4773 | || action == @selector(_insertOrderedList:) || action == @selector(_insertUnorderedList:) || action == @selector(_insertNestedOrderedList:) || action == @selector(_insertNestedUnorderedList:) |
| 4774 | || action == @selector(_increaseListLevel:) || action == @selector(_decreaseListLevel:) || action == @selector(_changeListType:) || action == @selector(_indent:) || action == @selector(_outdent:) |
| 4775 | || action == @selector(increaseSize:) || action == @selector(decreaseSize:) || action == @selector(makeTextWritingDirectionNatural:)) { |
| 4776 | // FIXME: This should be more nuanced in the future, rather than returning YES for all richly editable areas. For instance, outdent: should be disabled when the selection is already |
| 4777 | // at the outermost indentation level. |
| 4778 | return editorState.isContentRichlyEditable; |
| 4779 | } |
| 4780 | if (action == @selector(cut:)) |
| 4781 | return !editorState.isInPasswordField && editorState.isContentEditable && editorState.selectionType == WebCore::SelectionType::Range; |
| 4782 | |
| 4783 | if (action == @selector(paste:) || action == @selector(_pasteAsQuotation:) || action == @selector(_pasteAndMatchStyle:) || action == @selector(pasteAndMatchStyle:)) { |
| 4784 | if (editorState.selectionType == WebCore::SelectionType::None || !editorState.isContentEditable) |
| 4785 | return NO__objc_no; |
| 4786 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; |
| 4787 | NSArray *types = [self supportedPasteboardTypesForCurrentSelection]; |
| 4788 | NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])]; |
| 4789 | if ([pasteboard containsPasteboardTypes:types inItemSet:indices]) |
| 4790 | return YES__objc_yes; |
| 4791 | |
| 4792 | #if PLATFORM(IOS)(defined 1 && 1) || PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION) |
| 4793 | if (editorState.isContentRichlyEditable && protect(_page->preferences())->attachmentElementEnabled()) { |
| 4794 | for (NSItemProvider *itemProvider in pasteboard.itemProviders) { |
| 4795 | auto preferredPresentationStyle = itemProvider.preferredPresentationStyle; |
| 4796 | if (preferredPresentationStyle == UIPreferredPresentationStyleInline) |
| 4797 | continue; |
| 4798 | |
| 4799 | if (preferredPresentationStyle == UIPreferredPresentationStyleUnspecified && !itemProvider.suggestedName.length) |
| 4800 | continue; |
| 4801 | |
| 4802 | if (itemProvider.web_fileUploadContentTypes.count) |
| 4803 | return YES__objc_yes; |
| 4804 | } |
| 4805 | } |
| 4806 | #endif // PLATFORM(IOS) || PLATFORM(VISION) |
| 4807 | } |
| 4808 | |
| 4809 | if (action == @selector(copy:)) { |
| 4810 | if (editorState.isInPasswordField && !editorState.selectionIsRangeInAutoFilledAndViewableField) |
| 4811 | return NO__objc_no; |
| 4812 | return editorState.selectionType == WebCore::SelectionType::Range; |
| 4813 | } |
| 4814 | |
| 4815 | if (action == @selector(_define:) || action == @selector(define:) || action == @selector(lookup:)) { |
| 4816 | if (editorState.isInPasswordField || editorState.selectionType != WebCore::SelectionType::Range) |
| 4817 | return NO__objc_no; |
| 4818 | |
| 4819 | NSUInteger textLength = editorState.postLayoutData ? editorState.postLayoutData->selectedTextLength : 0; |
| 4820 | // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is |
| 4821 | // acceptable, but the interface takes a string. |
| 4822 | // <rdar://problem/15254406> |
| 4823 | if (!textLength || textLength > 200) |
| 4824 | return NO__objc_no; |
| 4825 | |
| 4826 | #if !PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 4827 | if ([(MCProfileConnection *)[PAL::getMCProfileConnectionClassSingleton() sharedConnection] effectiveBoolValueForSetting:PAL::get_ManagedConfiguration_MCFeatureDefinitionLookupAllowedSingleton()] == MCRestrictedBoolExplicitNo) |
| 4828 | return NO__objc_no; |
| 4829 | #endif |
| 4830 | |
| 4831 | return YES__objc_yes; |
| 4832 | } |
| 4833 | |
| 4834 | if (action == @selector(_lookup:)) { |
| 4835 | if (editorState.isInPasswordField) |
| 4836 | return NO__objc_no; |
| 4837 | |
| 4838 | #if !PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 4839 | if ([(MCProfileConnection *)[PAL::getMCProfileConnectionClassSingleton() sharedConnection] effectiveBoolValueForSetting:PAL::get_ManagedConfiguration_MCFeatureDefinitionLookupAllowedSingleton()] == MCRestrictedBoolExplicitNo) |
| 4840 | return NO__objc_no; |
| 4841 | #endif |
| 4842 | |
| 4843 | return editorState.selectionType == WebCore::SelectionType::Range; |
| 4844 | } |
| 4845 | |
| 4846 | if (action == @selector(_share:) || action == @selector(share:)) { |
| 4847 | if (editorState.isInPasswordField || editorState.selectionType != WebCore::SelectionType::Range) |
| 4848 | return NO__objc_no; |
| 4849 | |
| 4850 | return editorState.postLayoutData && editorState.postLayoutData->selectedTextLength > 0; |
| 4851 | } |
| 4852 | |
| 4853 | if (action == @selector(_addShortcut:) || action == @selector(addShortcut:)) { |
| 4854 | if (editorState.isInPasswordField || editorState.selectionType != WebCore::SelectionType::Range) |
| 4855 | return NO__objc_no; |
| 4856 | |
| 4857 | NSString *selectedText = [self selectedText]; |
| 4858 | if (![selectedText length]) |
| 4859 | return NO__objc_no; |
| 4860 | |
| 4861 | if (!UIKeyboardEnabledInputModesAllowOneToManyShortcuts()) |
| 4862 | return NO__objc_no; |
| 4863 | if (![selectedText _containsCJScripts]) |
| 4864 | return NO__objc_no; |
| 4865 | return YES__objc_yes; |
| 4866 | } |
| 4867 | |
| 4868 | if (action == @selector(_promptForReplace:) || action == @selector(promptForReplace:)) { |
| 4869 | if (editorState.selectionType != WebCore::SelectionType::Range || !editorState.postLayoutData || !editorState.postLayoutData->isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled]) |
| 4870 | return NO__objc_no; |
| 4871 | if ([[self selectedText] _containsCJScriptsOnly]) |
| 4872 | return NO__objc_no; |
| 4873 | return YES__objc_yes; |
| 4874 | } |
| 4875 | |
| 4876 | if (action == @selector(_transliterateChinese:) || action == @selector(transliterateChinese:)) { |
| 4877 | if (editorState.selectionType != WebCore::SelectionType::Range || !editorState.postLayoutData || !editorState.postLayoutData->isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled]) |
| 4878 | return NO__objc_no; |
| 4879 | return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]); |
| 4880 | } |
| 4881 | |
| 4882 | #if HAVE(TRANSLATION_UI_SERVICES)(defined 1 && 1) |
| 4883 | if (action == @selector(_translate:) || action == @selector(translate:)) { |
| 4884 | if (!PAL::isTranslationUIServicesFrameworkAvailable() || ![PAL::getLTUITranslationViewControllerClassSingleton() isAvailable]) |
| 4885 | return NO__objc_no; |
| 4886 | return !editorState.isInPasswordField && editorState.selectionType == WebCore::SelectionType::Range; |
| 4887 | } |
| 4888 | #endif // HAVE(TRANSLATION_UI_SERVICES) |
| 4889 | |
| 4890 | if (action == @selector(select:)) { |
| 4891 | // Disable select in password fields so that you can't see word boundaries. |
| 4892 | return !editorState.isInPasswordField && editorState.selectionType != WebCore::SelectionType::Range && self._hasContent; |
| 4893 | } |
| 4894 | |
| 4895 | auto isPreparingEditMenu = [&] { |
| 4896 | return [sender isKindOfClass:UIKeyCommand.class] && !_isInterpretingKeyEvent; |
| 4897 | }; |
| 4898 | |
| 4899 | if (action == @selector(selectAll:)) { |
| 4900 | if (isPreparingEditMenu()) { |
| 4901 | // By platform convention we don't show Select All in the edit menu for a range selection. |
| 4902 | return editorState.selectionType != WebCore::SelectionType::Range && self._hasContent; |
| 4903 | } |
| 4904 | return YES__objc_yes; |
| 4905 | } |
| 4906 | |
| 4907 | if (action == @selector(replace:)) |
| 4908 | return editorState.isContentEditable && !editorState.isInPasswordField; |
| 4909 | |
| 4910 | if (action == @selector(makeTextWritingDirectionLeftToRight:) || action == @selector(makeTextWritingDirectionRightToLeft:)) { |
| 4911 | if (!editorState.isContentEditable) |
| 4912 | return NO__objc_no; |
| 4913 | |
| 4914 | if (!editorState.postLayoutData) |
| 4915 | return NO__objc_no; |
| 4916 | auto baseWritingDirection = editorState.postLayoutData->baseWritingDirection; |
| 4917 | if (baseWritingDirection == WebCore::WritingDirection::LeftToRight && !UIKeyboardIsRightToLeftInputModeActive()) { |
| 4918 | // A keyboard is considered "active" if it is available for the user to switch to. As such, this check prevents |
| 4919 | // text direction actions from showing up in the case where a user has only added left-to-right keyboards, and |
| 4920 | // is also not editing right-to-left content. |
| 4921 | return NO__objc_no; |
| 4922 | } |
| 4923 | |
| 4924 | if (action == @selector(makeTextWritingDirectionLeftToRight:)) |
| 4925 | return baseWritingDirection != WebCore::WritingDirection::LeftToRight; |
| 4926 | |
| 4927 | return baseWritingDirection != WebCore::WritingDirection::RightToLeft; |
| 4928 | } |
| 4929 | |
| 4930 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 4931 | if (action == @selector(captureTextFromCamera:)) { |
| 4932 | if (!mayContainSelectableText(_focusedElementInformation.elementType) || _focusedElementInformation.isReadOnly) |
| 4933 | return NO__objc_no; |
| 4934 | |
| 4935 | if (isPreparingEditMenu() && editorState.selectionType == WebCore::SelectionType::Range) |
| 4936 | return NO__objc_no; |
| 4937 | } |
| 4938 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 4939 | |
| 4940 | #if HAVE(UIFINDINTERACTION)(defined 1 && 1) |
| 4941 | if (action == @selector(useSelectionForFind:) || action == @selector(findSelected:) || action == @selector(_findSelected:)) { |
| 4942 | if (!self.webView._findInteractionEnabled) |
| 4943 | return NO__objc_no; |
| 4944 | |
| 4945 | if (editorState.selectionType != WebCore::SelectionType::Range || !self.selectedText.length) |
| 4946 | return NO__objc_no; |
| 4947 | |
| 4948 | return YES__objc_yes; |
| 4949 | } |
| 4950 | #endif |
| 4951 | |
| 4952 | return [super canPerformAction:action withSender:sender]; |
| 4953 | } |
| 4954 | |
| 4955 | - (id)targetForAction:(SEL)action withSender:(id)sender |
| 4956 | { |
| 4957 | return [_webView.get() targetForAction:action withSender:sender]; |
| 4958 | } |
| 4959 | |
| 4960 | - (id)targetForActionForWebView:(SEL)action withSender:(id)sender |
| 4961 | { |
| 4962 | BOOL hasFallbackAsyncTextInputAction = action == @selector(_define:) || action == @selector(_translate:) || action == @selector(_lookup:); |
| 4963 | if (self.shouldUseAsyncInteractions && hasFallbackAsyncTextInputAction) |
| 4964 | return nilnullptr; |
| 4965 | return [super targetForAction:action withSender:sender]; |
| 4966 | } |
| 4967 | |
| 4968 | - (void)_willHideMenu:(NSNotification *)notification |
| 4969 | { |
| 4970 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 4971 | } |
| 4972 | |
| 4973 | - (void)_keyboardDidRequestDismissal:(NSNotification *)notification |
| 4974 | { |
| 4975 | if (_isEditable && [self isFirstResponder]) |
| 4976 | _keyboardDidRequestDismissal = YES__objc_yes; |
| 4977 | |
| 4978 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 4979 | [_fileUploadPanel repositionContextMenuIfNeeded:WebKit::KeyboardIsDismissing::Yes]; |
| 4980 | #endif |
| 4981 | } |
| 4982 | |
| 4983 | - (void)copyForWebView:(id)sender |
| 4984 | { |
| 4985 | protect(_page)->executeEditCommand("copy"_s); |
| 4986 | } |
| 4987 | |
| 4988 | - (void)cutForWebView:(id)sender |
| 4989 | { |
| 4990 | [self _executeEditCommand:@"cut"]; |
| 4991 | } |
| 4992 | |
| 4993 | - (void)pasteForWebView:(id)sender |
| 4994 | { |
| 4995 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 4996 | if (sender == UIMenuController.sharedMenuController && [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::GrantedForGesture]) |
| 4997 | return; |
| 4998 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 4999 | |
| 5000 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5001 | protect(_page)->executeEditCommand("paste"_s); |
| 5002 | } |
| 5003 | |
| 5004 | - (void)_pasteAsQuotationForWebView:(id)sender |
| 5005 | { |
| 5006 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5007 | protect(_page)->executeEditCommand("PasteAsQuotation"_s); |
| 5008 | } |
| 5009 | |
| 5010 | - (void)selectForWebView:(id)sender |
| 5011 | { |
| 5012 | if (!protect(_page->preferences())->textInteractionEnabled()) |
| 5013 | return; |
| 5014 | |
| 5015 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5016 | [_textInteractionWrapper selectWord]; |
| 5017 | // We cannot use selectWord command, because we want to be able to select the word even when it is the last in the paragraph. |
| 5018 | protect(_page)->extendSelection(WebCore::TextGranularity::WordGranularity, [] { }); |
| 5019 | } |
| 5020 | |
| 5021 | - (void)selectAllForWebView:(id)sender |
| 5022 | { |
| 5023 | if (!protect(_page->preferences())->textInteractionEnabled()) |
| 5024 | return; |
| 5025 | |
| 5026 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5027 | [_textInteractionWrapper selectAll:sender]; |
| 5028 | protect(_page)->selectAll(); |
| 5029 | } |
| 5030 | |
| 5031 | - (BOOL)shouldSynthesizeKeyEvents |
| 5032 | { |
| 5033 | if (_focusedElementInformation.shouldSynthesizeKeyEventsForEditing && self.hasHiddenContentEditable) |
| 5034 | return true; |
| 5035 | return false; |
| 5036 | } |
| 5037 | |
| 5038 | - (void)toggleBoldfaceForWebView:(id)sender |
| 5039 | { |
| 5040 | Ref page = *_page; |
| 5041 | if (!page->editorState().isContentRichlyEditable) |
| 5042 | return; |
| 5043 | |
| 5044 | [self _executeEditCommand:@"toggleBold"]; |
| 5045 | |
| 5046 | if (self.shouldSynthesizeKeyEvents) |
| 5047 | page->generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType::ToggleBoldface); |
| 5048 | } |
| 5049 | |
| 5050 | - (void)toggleItalicsForWebView:(id)sender |
| 5051 | { |
| 5052 | Ref page = *_page; |
| 5053 | if (!page->editorState().isContentRichlyEditable) |
| 5054 | return; |
| 5055 | |
| 5056 | [self _executeEditCommand:@"toggleItalic"]; |
| 5057 | |
| 5058 | if (self.shouldSynthesizeKeyEvents) |
| 5059 | page->generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType::ToggleItalic); |
| 5060 | } |
| 5061 | |
| 5062 | - (void)toggleUnderlineForWebView:(id)sender |
| 5063 | { |
| 5064 | if (!protect(_page)->editorState().isContentRichlyEditable) |
| 5065 | return; |
| 5066 | |
| 5067 | [self _executeEditCommand:@"toggleUnderline"]; |
| 5068 | |
| 5069 | if (self.shouldSynthesizeKeyEvents) |
| 5070 | protect(_page)->generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType::ToggleUnderline); |
| 5071 | } |
| 5072 | |
| 5073 | - (void)_defineForWebView:(id)sender |
| 5074 | { |
| 5075 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG179" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(5075, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 5076 | |
| 5077 | [self lookupForWebView:sender]; |
| 5078 | } |
| 5079 | |
| 5080 | - (void)accessibilityRetrieveSpeakSelectionContent |
| 5081 | { |
| 5082 | RetainPtr<WKContentView> view = self; |
| 5083 | RetainPtr<WKWebView> webView = _webView.get(); |
| 5084 | protect(_page)->getSelectionOrContentsAsString([view, webView](const String& string) { |
| 5085 | RetainPtr nsString = string.createNSString(); |
| 5086 | [webView _accessibilityDidGetSpeakSelectionContent:nsString.get()]; |
| 5087 | if ([view respondsToSelector:@selector(accessibilitySpeakSelectionSetContent:)]) |
| 5088 | [view accessibilitySpeakSelectionSetContent:nsString.get()]; |
| 5089 | }); |
| 5090 | } |
| 5091 | |
| 5092 | - (void)_accessibilityRetrieveRectsEnclosingSelectionOffset:(NSInteger)offset withGranularity:(UITextGranularity)granularity |
| 5093 | { |
| 5094 | protect(_page)->requestRectsForGranularityWithSelectionOffset(toWKTextGranularity(granularity), offset, [view = retainPtr(self), offset, granularity](const Vector<WebCore::SelectionGeometry>& selectionGeometries) { |
| 5095 | if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)]) |
| 5096 | [view _accessibilityDidGetSelectionRects:[view webSelectionRectsForSelectionGeometries:selectionGeometries] withGranularity:granularity atOffset:offset]; |
| 5097 | }); |
| 5098 | } |
| 5099 | |
| 5100 | - (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text |
| 5101 | { |
| 5102 | [self _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:nilnullptr]; |
| 5103 | } |
| 5104 | |
| 5105 | - (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(const Vector<WebCore::SelectionGeometry>& geometries))completionHandler |
| 5106 | { |
| 5107 | RetainPtr<WKContentView> view = self; |
| 5108 | protect(_page)->requestRectsAtSelectionOffsetWithText(offset, text, [view, offset, capturedCompletionHandler = makeBlockPtr(completionHandler)](const Vector<WebCore::SelectionGeometry>& selectionGeometries) { |
| 5109 | if (capturedCompletionHandler) |
| 5110 | capturedCompletionHandler(selectionGeometries); |
| 5111 | |
| 5112 | if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)]) |
| 5113 | [view _accessibilityDidGetSelectionRects:[view webSelectionRectsForSelectionGeometries:selectionGeometries] withGranularity:UITextGranularityWord atOffset:offset]; |
| 5114 | }); |
| 5115 | } |
| 5116 | |
| 5117 | - (void)_accessibilityStoreSelection |
| 5118 | { |
| 5119 | protect(_page)->storeSelectionForAccessibility(true); |
| 5120 | } |
| 5121 | |
| 5122 | - (void)_accessibilityClearSelection |
| 5123 | { |
| 5124 | protect(_page)->storeSelectionForAccessibility(false); |
| 5125 | } |
| 5126 | |
| 5127 | static UIPasteboardName pasteboardNameForAccessCategory(WebCore::DOMPasteAccessCategory pasteAccessCategory) |
| 5128 | { |
| 5129 | switch (pasteAccessCategory) { |
| 5130 | case WebCore::DOMPasteAccessCategory::General: |
| 5131 | case WebCore::DOMPasteAccessCategory::Fonts: |
| 5132 | return UIPasteboardNameGeneral; |
| 5133 | } |
| 5134 | } |
| 5135 | |
| 5136 | static UIPasteboard *pasteboardForAccessCategory(WebCore::DOMPasteAccessCategory pasteAccessCategory) |
| 5137 | { |
| 5138 | switch (pasteAccessCategory) { |
| 5139 | case WebCore::DOMPasteAccessCategory::General: |
| 5140 | case WebCore::DOMPasteAccessCategory::Fonts: |
| 5141 | return UIPasteboard.generalPasteboard; |
| 5142 | } |
| 5143 | } |
| 5144 | |
| 5145 | - (BOOL)_handleDOMPasteRequestWithResult:(WebCore::DOMPasteAccessResponse)response |
| 5146 | { |
| 5147 | if (auto pasteAccessCategory = std::exchange(_domPasteRequestCategory, std::nullopt)) { |
| 5148 | if (response == WebCore::DOMPasteAccessResponse::GrantedForCommand || response == WebCore::DOMPasteAccessResponse::GrantedForGesture) { |
| 5149 | if (auto replyID = protect(_page)->grantAccessToCurrentPasteboardData(pasteboardNameForAccessCategory(*pasteAccessCategory), [] () { })) |
| 5150 | protect(protect(protect(_page->websiteDataStore())->networkProcess())->connection())->waitForAsyncReplyAndDispatchImmediately<Messages::NetworkProcess::AllowFilesAccessFromWebProcess>(*replyID, 100_ms); |
| 5151 | } |
| 5152 | } |
| 5153 | |
| 5154 | if (auto pasteHandler = WTF::move(_domPasteRequestHandler)) { |
| 5155 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 5156 | [UIMenuController.sharedMenuController hideMenuFromView:self]; |
| 5157 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 5158 | pasteHandler(response); |
| 5159 | return YES__objc_yes; |
| 5160 | } |
| 5161 | return NO__objc_no; |
| 5162 | } |
| 5163 | |
| 5164 | - (void)_willPerformAction:(SEL)action sender:(id)sender |
| 5165 | { |
| 5166 | if (action != @selector(paste:)) |
| 5167 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 5168 | } |
| 5169 | |
| 5170 | - (void)_didPerformAction:(SEL)action sender:(id)sender |
| 5171 | { |
| 5172 | if (action == @selector(paste:)) |
| 5173 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 5174 | } |
| 5175 | |
| 5176 | // UIWKInteractionViewProtocol |
| 5177 | |
| 5178 | static inline WebKit::GestureType toGestureType(WKBEGestureTypeBEGestureType gestureType) |
| 5179 | { |
| 5180 | switch (gestureType) { |
| 5181 | case WKBEGestureTypeLoupeBEGestureTypeLoupe: |
| 5182 | return WebKit::GestureType::Loupe; |
| 5183 | case WKBEGestureTypeOneFingerTapBEGestureTypeOneFingerTap: |
| 5184 | return WebKit::GestureType::OneFingerTap; |
| 5185 | case WKBEGestureTypeDoubleTapAndHoldBEGestureTypeDoubleTapAndHold: |
| 5186 | return WebKit::GestureType::TapAndAHalf; |
| 5187 | case WKBEGestureTypeDoubleTapBEGestureTypeDoubleTap: |
| 5188 | return WebKit::GestureType::DoubleTap; |
| 5189 | case WKBEGestureTypeOneFingerDoubleTapBEGestureTypeOneFingerDoubleTap: |
| 5190 | return WebKit::GestureType::OneFingerDoubleTap; |
| 5191 | case WKBEGestureTypeOneFingerTripleTapBEGestureTypeOneFingerTripleTap: |
| 5192 | return WebKit::GestureType::OneFingerTripleTap; |
| 5193 | case WKBEGestureTypeTwoFingerSingleTapBEGestureTypeTwoFingerSingleTap: |
| 5194 | return WebKit::GestureType::TwoFingerSingleTap; |
| 5195 | case WKBEGestureTypeIMPhraseBoundaryDragBEGestureTypeIMPhraseBoundaryDrag: |
| 5196 | return WebKit::GestureType::PhraseBoundary; |
| 5197 | default: |
| 5198 | ASSERT_NOT_REACHED()((void)0); |
| 5199 | return WebKit::GestureType::Loupe; |
| 5200 | } |
| 5201 | } |
| 5202 | |
| 5203 | static inline WKBEGestureTypeBEGestureType toWKBEGestureType(WebKit::GestureType gestureType) |
| 5204 | { |
| 5205 | switch (gestureType) { |
| 5206 | case WebKit::GestureType::Loupe: |
| 5207 | return WKBEGestureTypeLoupeBEGestureTypeLoupe; |
| 5208 | case WebKit::GestureType::OneFingerTap: |
| 5209 | return WKBEGestureTypeOneFingerTapBEGestureTypeOneFingerTap; |
| 5210 | case WebKit::GestureType::TapAndAHalf: |
| 5211 | return WKBEGestureTypeDoubleTapAndHoldBEGestureTypeDoubleTapAndHold; |
| 5212 | case WebKit::GestureType::DoubleTap: |
| 5213 | return WKBEGestureTypeDoubleTapBEGestureTypeDoubleTap; |
| 5214 | case WebKit::GestureType::OneFingerDoubleTap: |
| 5215 | return WKBEGestureTypeOneFingerDoubleTapBEGestureTypeOneFingerDoubleTap; |
| 5216 | case WebKit::GestureType::OneFingerTripleTap: |
| 5217 | return WKBEGestureTypeOneFingerTripleTapBEGestureTypeOneFingerTripleTap; |
| 5218 | case WebKit::GestureType::TwoFingerSingleTap: |
| 5219 | return WKBEGestureTypeTwoFingerSingleTapBEGestureTypeTwoFingerSingleTap; |
| 5220 | case WebKit::GestureType::PhraseBoundary: |
| 5221 | return WKBEGestureTypeIMPhraseBoundaryDragBEGestureTypeIMPhraseBoundaryDrag; |
| 5222 | } |
| 5223 | } |
| 5224 | |
| 5225 | static TextStream& operator<<(TextStream& stream, WebKit::GestureType gestureType) |
| 5226 | { |
| 5227 | switch (gestureType) { |
| 5228 | case WebKit::GestureType::Loupe: stream << "Loupe"; break; |
| 5229 | case WebKit::GestureType::OneFingerTap: stream << "OneFingerTap"; break; |
| 5230 | case WebKit::GestureType::TapAndAHalf: stream << "TapAndAHalf"; break; |
| 5231 | case WebKit::GestureType::DoubleTap: stream << "DoubleTap"; break; |
| 5232 | case WebKit::GestureType::OneFingerDoubleTap: stream << "OneFingerDoubleTap"; break; |
| 5233 | case WebKit::GestureType::OneFingerTripleTap: stream << "OneFingerTripleTap"; break; |
| 5234 | case WebKit::GestureType::TwoFingerSingleTap: stream << "TwoFingerSingleTap"; break; |
| 5235 | case WebKit::GestureType::PhraseBoundary: stream << "PhraseBoundary"; break; |
| 5236 | } |
| 5237 | |
| 5238 | return stream; |
| 5239 | } |
| 5240 | |
| 5241 | static inline WebKit::SelectionTouch toSelectionTouch(WKBESelectionTouchPhaseBESelectionTouchPhase touch) |
| 5242 | { |
| 5243 | switch (touch) { |
| 5244 | case WKBESelectionTouchPhaseStartedBESelectionTouchPhaseStarted: |
| 5245 | return WebKit::SelectionTouch::Started; |
| 5246 | case WKBESelectionTouchPhaseMovedBESelectionTouchPhaseMoved: |
| 5247 | return WebKit::SelectionTouch::Moved; |
| 5248 | case WKBESelectionTouchPhaseEndedBESelectionTouchPhaseEnded: |
| 5249 | return WebKit::SelectionTouch::Ended; |
| 5250 | case WKBESelectionTouchPhaseEndedMovingForwardBESelectionTouchPhaseEndedMovingForward: |
| 5251 | return WebKit::SelectionTouch::EndedMovingForward; |
| 5252 | case WKBESelectionTouchPhaseEndedMovingBackwardBESelectionTouchPhaseEndedMovingBackward: |
| 5253 | return WebKit::SelectionTouch::EndedMovingBackward; |
| 5254 | case WKBESelectionTouchPhaseEndedNotMovingBESelectionTouchPhaseEndedNotMoving: |
| 5255 | return WebKit::SelectionTouch::EndedNotMoving; |
| 5256 | } |
| 5257 | ASSERT_NOT_REACHED()((void)0); |
| 5258 | return WebKit::SelectionTouch::Ended; |
| 5259 | } |
| 5260 | |
| 5261 | static inline WKBESelectionTouchPhaseBESelectionTouchPhase toWKBESelectionTouchPhase(WebKit::SelectionTouch touch) |
| 5262 | { |
| 5263 | switch (touch) { |
| 5264 | case WebKit::SelectionTouch::Started: |
| 5265 | return WKBESelectionTouchPhaseStartedBESelectionTouchPhaseStarted; |
| 5266 | case WebKit::SelectionTouch::Moved: |
| 5267 | return WKBESelectionTouchPhaseMovedBESelectionTouchPhaseMoved; |
| 5268 | case WebKit::SelectionTouch::Ended: |
| 5269 | return WKBESelectionTouchPhaseEndedBESelectionTouchPhaseEnded; |
| 5270 | case WebKit::SelectionTouch::EndedMovingForward: |
| 5271 | return WKBESelectionTouchPhaseEndedMovingForwardBESelectionTouchPhaseEndedMovingForward; |
| 5272 | case WebKit::SelectionTouch::EndedMovingBackward: |
| 5273 | return WKBESelectionTouchPhaseEndedMovingBackwardBESelectionTouchPhaseEndedMovingBackward; |
| 5274 | case WebKit::SelectionTouch::EndedNotMoving: |
| 5275 | return WKBESelectionTouchPhaseEndedNotMovingBESelectionTouchPhaseEndedNotMoving; |
| 5276 | } |
| 5277 | } |
| 5278 | |
| 5279 | static TextStream& operator<<(TextStream& stream, WebKit::SelectionTouch touch) |
| 5280 | { |
| 5281 | switch (touch) { |
| 5282 | case WebKit::SelectionTouch::Started: stream << "Started"; break; |
| 5283 | case WebKit::SelectionTouch::Moved: stream << "Moved"; break; |
| 5284 | case WebKit::SelectionTouch::Ended: stream << "Ended"; break; |
| 5285 | case WebKit::SelectionTouch::EndedMovingForward: stream << "EndedMovingForward"; break; |
| 5286 | case WebKit::SelectionTouch::EndedMovingBackward: stream << "EndedMovingBackward"; break; |
| 5287 | case WebKit::SelectionTouch::EndedNotMoving: stream << "EndedNotMoving"; break; |
| 5288 | } |
| 5289 | |
| 5290 | return stream; |
| 5291 | } |
| 5292 | |
| 5293 | static inline WebKit::GestureRecognizerState toGestureRecognizerState(UIGestureRecognizerState state) |
| 5294 | { |
| 5295 | switch (state) { |
| 5296 | case UIGestureRecognizerStatePossible: |
| 5297 | return WebKit::GestureRecognizerState::Possible; |
| 5298 | case UIGestureRecognizerStateBegan: |
| 5299 | return WebKit::GestureRecognizerState::Began; |
| 5300 | case UIGestureRecognizerStateChanged: |
| 5301 | return WebKit::GestureRecognizerState::Changed; |
| 5302 | case UIGestureRecognizerStateCancelled: |
| 5303 | return WebKit::GestureRecognizerState::Cancelled; |
| 5304 | case UIGestureRecognizerStateEnded: |
| 5305 | return WebKit::GestureRecognizerState::Ended; |
| 5306 | case UIGestureRecognizerStateFailed: |
| 5307 | return WebKit::GestureRecognizerState::Failed; |
| 5308 | } |
| 5309 | } |
| 5310 | |
| 5311 | static inline UIGestureRecognizerState toUIGestureRecognizerState(WebKit::GestureRecognizerState state) |
| 5312 | { |
| 5313 | switch (state) { |
| 5314 | case WebKit::GestureRecognizerState::Possible: |
| 5315 | return UIGestureRecognizerStatePossible; |
| 5316 | case WebKit::GestureRecognizerState::Began: |
| 5317 | return UIGestureRecognizerStateBegan; |
| 5318 | case WebKit::GestureRecognizerState::Changed: |
| 5319 | return UIGestureRecognizerStateChanged; |
| 5320 | case WebKit::GestureRecognizerState::Cancelled: |
| 5321 | return UIGestureRecognizerStateCancelled; |
| 5322 | case WebKit::GestureRecognizerState::Ended: |
| 5323 | return UIGestureRecognizerStateEnded; |
| 5324 | case WebKit::GestureRecognizerState::Failed: |
| 5325 | return UIGestureRecognizerStateFailed; |
| 5326 | } |
| 5327 | } |
| 5328 | |
| 5329 | static TextStream& operator<<(TextStream& stream, WebKit::GestureRecognizerState state) |
| 5330 | { |
| 5331 | switch (state) { |
| 5332 | case WebKit::GestureRecognizerState::Possible: stream << "Possible"; break; |
| 5333 | case WebKit::GestureRecognizerState::Began: stream << "Began"; break; |
| 5334 | case WebKit::GestureRecognizerState::Changed: stream << "Changed"; break; |
| 5335 | case WebKit::GestureRecognizerState::Cancelled: stream << "Cancelled"; break; |
| 5336 | case WebKit::GestureRecognizerState::Ended: stream << "Ended"; break; |
| 5337 | case WebKit::GestureRecognizerState::Failed: stream << "Failed"; break; |
| 5338 | } |
| 5339 | |
| 5340 | return stream; |
| 5341 | } |
| 5342 | |
| 5343 | static inline WKBESelectionFlagsBESelectionFlags toWKBESelectionFlags(OptionSet<WebKit::SelectionFlags> flags) |
| 5344 | { |
| 5345 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 5346 | NSUInteger uiFlags = WKBESelectionFlagsNoneBESelectionFlagsNone; |
| 5347 | #else |
| 5348 | NSInteger uiFlags = UIWKNone; |
| 5349 | #endif |
| 5350 | if (flags.contains(WebKit::SelectionFlags::WordIsNearTap)) |
| 5351 | uiFlags |= WKBEWordIsNearTapBEWordIsNearTap; |
| 5352 | if (flags.contains(WebKit::SelectionFlags::SelectionFlipped)) |
| 5353 | uiFlags |= WKBESelectionFlippedBESelectionFlipped; |
| 5354 | if (flags.contains(WebKit::SelectionFlags::PhraseBoundaryChanged)) |
| 5355 | uiFlags |= WKBEPhraseBoundaryChangedBEPhraseBoundaryChanged; |
| 5356 | |
| 5357 | return static_cast<WKBESelectionFlagsBESelectionFlags>(uiFlags); |
| 5358 | } |
| 5359 | |
| 5360 | static inline OptionSet<WebKit::SelectionFlags> toSelectionFlags(WKBESelectionFlagsBESelectionFlags uiFlags) |
| 5361 | { |
| 5362 | OptionSet<WebKit::SelectionFlags> flags; |
| 5363 | if (uiFlags & WKBEWordIsNearTapBEWordIsNearTap) |
| 5364 | flags.add(WebKit::SelectionFlags::WordIsNearTap); |
| 5365 | if (uiFlags & WKBESelectionFlippedBESelectionFlipped) |
| 5366 | flags.add(WebKit::SelectionFlags::SelectionFlipped); |
| 5367 | if (uiFlags & WKBEPhraseBoundaryChangedBEPhraseBoundaryChanged) |
| 5368 | flags.add(WebKit::SelectionFlags::PhraseBoundaryChanged); |
| 5369 | return flags; |
| 5370 | } |
| 5371 | |
| 5372 | static TextStream& operator<<(TextStream& stream, OptionSet<WebKit::SelectionFlags> flags) |
| 5373 | { |
| 5374 | bool didAppend = false; |
| 5375 | |
| 5376 | auto appendIf = [&](auto flag, auto message) { |
| 5377 | if (!flags.contains(flag)) |
| 5378 | return; |
| 5379 | if (didAppend) |
| 5380 | stream << "|"; |
| 5381 | stream << message; |
| 5382 | didAppend = true; |
| 5383 | }; |
| 5384 | |
| 5385 | appendIf(WebKit::SelectionFlags::WordIsNearTap, "WordIsNearTap"); |
| 5386 | appendIf(WebKit::SelectionFlags::SelectionFlipped, "SelectionFlipped"); |
| 5387 | appendIf(WebKit::SelectionFlags::PhraseBoundaryChanged, "PhraseBoundaryChanged"); |
| 5388 | |
| 5389 | if (!didAppend) |
| 5390 | stream << "None"; |
| 5391 | |
| 5392 | return stream; |
| 5393 | } |
| 5394 | |
| 5395 | static inline WebCore::TextGranularity toWKTextGranularity(UITextGranularity granularity) |
| 5396 | { |
| 5397 | switch (granularity) { |
| 5398 | case UITextGranularityCharacter: |
| 5399 | return WebCore::TextGranularity::CharacterGranularity; |
| 5400 | case UITextGranularityWord: |
| 5401 | return WebCore::TextGranularity::WordGranularity; |
| 5402 | case UITextGranularitySentence: |
| 5403 | return WebCore::TextGranularity::SentenceGranularity; |
| 5404 | case UITextGranularityParagraph: |
| 5405 | return WebCore::TextGranularity::ParagraphGranularity; |
| 5406 | case UITextGranularityLine: |
| 5407 | return WebCore::TextGranularity::LineGranularity; |
| 5408 | case UITextGranularityDocument: |
| 5409 | return WebCore::TextGranularity::DocumentGranularity; |
| 5410 | } |
| 5411 | } |
| 5412 | |
| 5413 | static inline WebCore::SelectionDirection toWKSelectionDirection(UITextDirection direction) |
| 5414 | { |
| 5415 | switch (direction) { |
| 5416 | case UITextLayoutDirectionDown: |
| 5417 | case UITextLayoutDirectionRight: |
| 5418 | return WebCore::SelectionDirection::Right; |
| 5419 | case UITextLayoutDirectionUp: |
| 5420 | case UITextLayoutDirectionLeft: |
| 5421 | return WebCore::SelectionDirection::Left; |
| 5422 | default: |
| 5423 | // UITextDirection is not an enum, but we only want to accept values from UITextLayoutDirection. |
| 5424 | ASSERT_NOT_REACHED()((void)0); |
| 5425 | return WebCore::SelectionDirection::Right; |
| 5426 | } |
| 5427 | } |
| 5428 | |
| 5429 | static void selectionChangedWithGesture(WKTextInteractionWrapper *interaction, const WebCore::IntPoint& point, WebKit::GestureType gestureType, WebKit::GestureRecognizerState gestureState, OptionSet<WebKit::SelectionFlags> flags) |
| 5430 | { |
| 5431 | [interaction selectionChangedWithGestureAt:(CGPoint)point withGesture:toWKBEGestureType(gestureType) withState:toUIGestureRecognizerState(gestureState) withFlags:toWKBESelectionFlags(flags)]; |
| 5432 | } |
| 5433 | |
| 5434 | static void selectionChangedWithTouch(WKTextInteractionWrapper *interaction, const WebCore::IntPoint& point, WebKit::SelectionTouch touch, OptionSet<WebKit::SelectionFlags> flags) |
| 5435 | { |
| 5436 | [interaction selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toWKBESelectionTouchPhase(touch) withFlags:toWKBESelectionFlags(flags)]; |
| 5437 | } |
| 5438 | |
| 5439 | - (BOOL)_hasFocusedElement |
| 5440 | { |
| 5441 | return _focusedElementInformation.elementType != WebKit::InputType::None; |
| 5442 | } |
| 5443 | |
| 5444 | - (BOOL)_isSameAsFocusedElement:(const WebCore::ElementContext&)element |
| 5445 | { |
| 5446 | return [self _hasFocusedElement] && _focusedElementInformation.elementContext.isSameElement(element); |
| 5447 | } |
| 5448 | |
| 5449 | - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(WKBEGestureTypeBEGestureType)gestureType withState:(UIGestureRecognizerState)state |
| 5450 | { |
| 5451 | [self changeSelectionWithGestureAt:point withGesture:gestureType withState:state withFlags:UIWKNone]; |
| 5452 | } |
| 5453 | |
| 5454 | - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(WKBEGestureTypeBEGestureType)gestureType withState:(UIGestureRecognizerState)state withFlags:(WKBESelectionFlagsBESelectionFlags)flags |
| 5455 | { |
| 5456 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point, state, gestureType, std::nullopt, flags); |
| 5457 | |
| 5458 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5459 | _usingGestureForSelection = YES__objc_yes; |
| 5460 | protect(_page)->selectWithGesture(WebCore::IntPoint(point), toGestureType(gestureType), toGestureRecognizerState(state), self._hasFocusedElement, [self, strongSelf = retainPtr(self), state, flags](const WebCore::IntPoint& point, WebKit::GestureType gestureType, WebKit::GestureRecognizerState gestureState, OptionSet<WebKit::SelectionFlags> innerFlags) { |
| 5461 | selectionChangedWithGesture(_textInteractionWrapper.get(), point, gestureType, gestureState, toSelectionFlags(flags) | innerFlags); |
| 5462 | if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled) |
| 5463 | _usingGestureForSelection = NO__objc_no; |
| 5464 | }); |
| 5465 | } |
| 5466 | |
| 5467 | - (void)updateSelection |
| 5468 | { |
| 5469 | switch (_selectionInteractionType) { |
| 5470 | case SelectionInteractionType::None: |
| 5471 | break; |
| 5472 | case SelectionInteractionType::Touch: |
| 5473 | [self updateSelectionWithTouchAt:[self convertPoint:_lastSelectionTouch.point fromView:self.webView] withSelectionTouch:WKBESelectionTouchPhaseMovedBESelectionTouchPhaseMoved baseIsStart:_lastSelectionTouch.baseIsStart withFlags:_lastSelectionTouch.flags]; |
| 5474 | break; |
| 5475 | case SelectionInteractionType::ExtentPoint: |
| 5476 | [self updateSelectionWithExtentPoint:[self convertPoint:_lastSelectionExtentPoint.point fromView:self.webView] hasFocusedElement:self._hasFocusedElement respectSelectionAnchor:_lastSelectionExtentPoint.respectSelectionAnchor completionHandler:^(BOOL selectionEndIsMoving) { }]; |
| 5477 | break; |
| 5478 | case SelectionInteractionType::ExtentPointAndBoundary: |
| 5479 | [self updateSelectionWithExtentPointAndBoundary:[self convertPoint:_lastSelectionExtentPointAndBoundary.point fromView:self.webView] textGranularity:_lastSelectionExtentPointAndBoundary.granularity textInteractionSource:_lastSelectionExtentPointAndBoundary.interactionSource completionHandler:^(BOOL selectionEndIsMoving) { }]; |
| 5480 | break; |
| 5481 | } |
| 5482 | } |
| 5483 | |
| 5484 | - (void)changeSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(WKBESelectionTouchPhaseBESelectionTouchPhase)touch baseIsStart:(BOOL)baseIsStart withFlags:(WKBESelectionFlagsBESelectionFlags)flags |
| 5485 | { |
| 5486 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point, std::nullopt, std::nullopt, touch, flags); |
| 5487 | |
| 5488 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5489 | _usingGestureForSelection = YES__objc_yes; |
| 5490 | |
| 5491 | if (_page->isAutoscrolling()) { |
| 5492 | auto touchType = toSelectionTouch(touch); |
| 5493 | switch (touchType) { |
| 5494 | case WebKit::SelectionTouch::Started: |
| 5495 | case WebKit::SelectionTouch::Moved: |
| 5496 | _selectionInteractionType = SelectionInteractionType::Touch; |
| 5497 | _lastSelectionTouch.point = [self convertPoint:point toView:self.webView]; |
| 5498 | _lastSelectionTouch.baseIsStart = baseIsStart; |
| 5499 | _lastSelectionTouch.flags = flags; |
| 5500 | break; |
| 5501 | case WebKit::SelectionTouch::Ended: |
| 5502 | case WebKit::SelectionTouch::EndedMovingForward: |
| 5503 | case WebKit::SelectionTouch::EndedMovingBackward: |
| 5504 | case WebKit::SelectionTouch::EndedNotMoving: |
| 5505 | _selectionInteractionType = SelectionInteractionType::None; |
| 5506 | break; |
| 5507 | } |
| 5508 | } |
| 5509 | |
| 5510 | [self updateSelectionWithTouchAt:point withSelectionTouch:touch baseIsStart:baseIsStart withFlags:flags]; |
| 5511 | |
| 5512 | } |
| 5513 | |
| 5514 | - (void)updateSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(WKBESelectionTouchPhaseBESelectionTouchPhase)touch baseIsStart:(BOOL)baseIsStart withFlags:(WKBESelectionFlagsBESelectionFlags)flags |
| 5515 | { |
| 5516 | protect(_page)->updateSelectionWithTouches(WebCore::IntPoint(point), toSelectionTouch(touch), baseIsStart, [self, strongSelf = retainPtr(self), flags](const WebCore::IntPoint& point, WebKit::SelectionTouch touch, OptionSet<WebKit::SelectionFlags> innerFlags) { |
| 5517 | if (_selectionInteractionType == SelectionInteractionType::Touch && innerFlags.contains(WebKit::SelectionFlags::SelectionFlipped)) |
| 5518 | _lastSelectionTouch.baseIsStart = !_lastSelectionTouch.baseIsStart; |
| 5519 | selectionChangedWithTouch(_textInteractionWrapper.get(), point, touch, toSelectionFlags(flags) | innerFlags); |
| 5520 | if (toWKBESelectionTouchPhase(touch) != WKBESelectionTouchPhaseStartedBESelectionTouchPhaseStarted && toWKBESelectionTouchPhase(touch) != WKBESelectionTouchPhaseMovedBESelectionTouchPhaseMoved) |
| 5521 | _usingGestureForSelection = NO__objc_no; |
| 5522 | }); |
| 5523 | } |
| 5524 | |
| 5525 | - (void)changeSelectionWithTouchesFrom:(CGPoint)from to:(CGPoint)to withGesture:(WKBEGestureTypeBEGestureType)gestureType withState:(UIGestureRecognizerState)gestureState |
| 5526 | { |
| 5527 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, from, gestureState, gestureType); |
| 5528 | |
| 5529 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5530 | _usingGestureForSelection = YES__objc_yes; |
| 5531 | protect(_page)->selectWithTwoTouches(WebCore::IntPoint(from), WebCore::IntPoint(to), toGestureType(gestureType), toGestureRecognizerState(gestureState), [self, strongSelf = retainPtr(self)](const WebCore::IntPoint& point, WebKit::GestureType gestureType, WebKit::GestureRecognizerState gestureState, OptionSet<WebKit::SelectionFlags> flags) { |
| 5532 | selectionChangedWithGesture(_textInteractionWrapper.get(), point, gestureType, gestureState, flags); |
| 5533 | if (toUIGestureRecognizerState(gestureState) == UIGestureRecognizerStateEnded || toUIGestureRecognizerState(gestureState) == UIGestureRecognizerStateCancelled) |
| 5534 | _usingGestureForSelection = NO__objc_no; |
| 5535 | }); |
| 5536 | } |
| 5537 | |
| 5538 | - (void)moveByOffset:(NSInteger)offset |
| 5539 | { |
| 5540 | if (!offset) |
| 5541 | return; |
| 5542 | |
| 5543 | [self _internalBeginSelectionChange]; |
| 5544 | RetainPtr<WKContentView> view = self; |
| 5545 | protect(_page)->moveSelectionByOffset(offset, [view] { |
| 5546 | [view _internalEndSelectionChange]; |
| 5547 | }); |
| 5548 | } |
| 5549 | |
| 5550 | - (const WebKit::WKAutoCorrectionData&)autocorrectionData |
| 5551 | { |
| 5552 | return _autocorrectionData; |
| 5553 | } |
| 5554 | |
| 5555 | // The completion handler can pass nil if input does not match the actual text preceding the insertion point. |
| 5556 | - (void)requestAutocorrectionRectsForString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForInput))completionHandler |
| 5557 | { |
| 5558 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG180" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(5558, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 5559 | |
| 5560 | if (!completionHandler) { |
| 5561 | [NSException raise:NSInvalidArgumentException format:@"Expected a nonnull completion handler in %s.", __PRETTY_FUNCTION__]; |
| 5562 | return; |
| 5563 | } |
| 5564 | |
| 5565 | [self _internalRequestTextRectsForString:input completion:[view = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)](auto& rects) { |
| 5566 | completionHandler(!rects.isEmpty() ? [WKAutocorrectionRects autocorrectionRectsWithFirstCGRect:rects.first() lastCGRect:rects.last()] : nilnullptr); |
| 5567 | }]; |
| 5568 | } |
| 5569 | |
| 5570 | - (void)_internalRequestTextRectsForString:(NSString *)input completion:(Function<void(const Vector<WebCore::FloatRect>&)>&&)completion |
| 5571 | { |
| 5572 | if (!input.length) |
| 5573 | return completion({ }); |
| 5574 | |
| 5575 | protect(_page)->requestAutocorrectionData(input, [view = retainPtr(self), completion = WTF::move(completion)](const WebKit::WebAutocorrectionData& data) mutable { |
| 5576 | CGRect firstRect; |
| 5577 | CGRect lastRect; |
| 5578 | auto& rects = data.textRects; |
| 5579 | if (rects.isEmpty()) { |
| 5580 | firstRect = CGRectZero; |
| 5581 | lastRect = CGRectZero; |
| 5582 | } else { |
| 5583 | firstRect = rects.first(); |
| 5584 | lastRect = rects.last(); |
| 5585 | } |
| 5586 | |
| 5587 | view->_autocorrectionData.font = data.font; |
| 5588 | view->_autocorrectionData.textFirstRect = firstRect; |
| 5589 | view->_autocorrectionData.textLastRect = lastRect; |
| 5590 | |
| 5591 | completion(rects); |
| 5592 | }); |
| 5593 | } |
| 5594 | |
| 5595 | #if HAVE(UI_EDIT_MENU_INTERACTION)(defined 1 && 1) |
| 5596 | |
| 5597 | - (void)requestPreferredArrowDirectionForEditMenuWithCompletionHandler:(void(^)(UIEditMenuArrowDirection))completion |
| 5598 | { |
| 5599 | if (self.shouldSuppressEditMenu) { |
| 5600 | completion(UIEditMenuArrowDirectionAutomatic); |
| 5601 | return; |
| 5602 | } |
| 5603 | |
| 5604 | auto requestRectsToEvadeIfNeeded = [startTime = ApproximateTime::now(), weakSelf = WeakObjCPtr<WKContentView>(self), completion = makeBlockPtr(completion)]() mutable { |
| 5605 | auto strongSelf = weakSelf.get(); |
| 5606 | if (!strongSelf) { |
| 5607 | completion(UIEditMenuArrowDirectionAutomatic); |
| 5608 | return; |
| 5609 | } |
| 5610 | |
| 5611 | if ([strongSelf webView]._editable) { |
| 5612 | completion(UIEditMenuArrowDirectionAutomatic); |
| 5613 | return; |
| 5614 | } |
| 5615 | |
| 5616 | auto focusedElementType = strongSelf->_focusedElementInformation.elementType; |
| 5617 | if (focusedElementType != WebKit::InputType::ContentEditable && focusedElementType != WebKit::InputType::TextArea) { |
| 5618 | completion(UIEditMenuArrowDirectionAutomatic); |
| 5619 | return; |
| 5620 | } |
| 5621 | |
| 5622 | // Give the page some time to present custom editing UI before attempting to detect and evade it. |
| 5623 | auto delayBeforeShowingEditMenu = std::max(0_s, 0.25_s - (ApproximateTime::now() - startTime)); |
| 5624 | WorkQueue::mainSingleton().dispatchAfter(delayBeforeShowingEditMenu, [completion = WTF::move(completion), weakSelf]() mutable { |
| 5625 | auto strongSelf = weakSelf.get(); |
| 5626 | if (!strongSelf) { |
| 5627 | completion(UIEditMenuArrowDirectionAutomatic); |
| 5628 | return; |
| 5629 | } |
| 5630 | |
| 5631 | if (!strongSelf->_page) { |
| 5632 | completion(UIEditMenuArrowDirectionAutomatic); |
| 5633 | return; |
| 5634 | } |
| 5635 | |
| 5636 | protect(strongSelf->_page)->requestEvasionRectsAboveSelection([completion = WTF::move(completion)](auto& rects) mutable { |
| 5637 | completion(rects.isEmpty() ? UIEditMenuArrowDirectionAutomatic : UIEditMenuArrowDirectionUp); |
| 5638 | }); |
| 5639 | }); |
| 5640 | }; |
| 5641 | |
| 5642 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 5643 | [self doAfterComputingImageAnalysisResultsForBackgroundRemoval:WTF::move(requestRectsToEvadeIfNeeded)]; |
| 5644 | #else |
| 5645 | requestRectsToEvadeIfNeeded(); |
| 5646 | #endif |
| 5647 | } |
| 5648 | |
| 5649 | #endif // HAVE(UI_EDIT_MENU_INTERACTION) |
| 5650 | |
| 5651 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 5652 | |
| 5653 | - (UIMenu *)removeBackgroundMenu |
| 5654 | { |
| 5655 | if (!_page || !protect(_page->preferences())->removeBackgroundEnabled()) |
| 5656 | return nilnullptr; |
| 5657 | |
| 5658 | if (!protect(_page)->editorState().hasPostLayoutData() || !_removeBackgroundData) |
| 5659 | return nilnullptr; |
| 5660 | |
| 5661 | if (_removeBackgroundData->element != protect(_page)->editorState().postLayoutData->selectedEditableImage) |
| 5662 | return nilnullptr; |
| 5663 | |
| 5664 | return [self menuWithInlineAction:WebCore::contextMenuItemTitleRemoveBackground().createNSString().get() image:[UIImage _systemImageNamed:@"circle.rectangle.filled.pattern.diagonalline"] identifier:@"WKActionRemoveBackground" handler:[](WKContentView *view) { |
| 5665 | auto data = std::exchange(view->_removeBackgroundData, { }); |
| 5666 | if (!data) |
| 5667 | return; |
| 5668 | |
| 5669 | auto [elementContext, image, preferredMIMEType] = *data; |
| 5670 | if (auto [data, type] = WebKit::imageDataForRemoveBackground(image.get(), preferredMIMEType.createCFString().get()); data) |
| 5671 | protect(view->_page)->replaceImageForRemoveBackground(elementContext, { String { type.get() } }, span(data.get())); |
| 5672 | }]; |
| 5673 | } |
| 5674 | |
| 5675 | - (void)doAfterComputingImageAnalysisResultsForBackgroundRemoval:(CompletionHandler<void()>&&)completion |
| 5676 | { |
| 5677 | Ref page = *_page; |
| 5678 | if (!page->editorState().hasPostLayoutData()) { |
| 5679 | completion(); |
| 5680 | return; |
| 5681 | } |
| 5682 | |
| 5683 | auto elementToAnalyze = page->editorState().postLayoutData->selectedEditableImage; |
| 5684 | if (_removeBackgroundData && _removeBackgroundData->element == elementToAnalyze) { |
| 5685 | completion(); |
| 5686 | return; |
| 5687 | } |
| 5688 | |
| 5689 | _removeBackgroundData = std::nullopt; |
| 5690 | |
| 5691 | if (!elementToAnalyze) { |
| 5692 | completion(); |
| 5693 | return; |
| 5694 | } |
| 5695 | |
| 5696 | protect(_page)->shouldAllowRemoveBackground(*elementToAnalyze, [context = *elementToAnalyze, completion = WTF::move(completion), weakSelf = WeakObjCPtr<WKContentView>(self)](bool shouldAllow) mutable { |
| 5697 | auto strongSelf = weakSelf.get(); |
| 5698 | if (!shouldAllow || !strongSelf) { |
| 5699 | completion(); |
| 5700 | return; |
| 5701 | } |
| 5702 | |
| 5703 | protect(strongSelf->_page)->requestImageBitmap(context, [context, completion = WTF::move(completion), weakSelf = WTF::move(weakSelf)](std::optional<WebCore::ShareableBitmapHandle>&& imageData, auto& sourceMIMEType) mutable { |
| 5704 | auto strongSelf = weakSelf.get(); |
| 5705 | if (!strongSelf) { |
| 5706 | completion(); |
| 5707 | return; |
| 5708 | } |
| 5709 | |
| 5710 | if (!imageData) { |
| 5711 | completion(); |
| 5712 | return; |
| 5713 | } |
| 5714 | |
| 5715 | auto imageBitmap = WebCore::ShareableBitmap::create(WTF::move(*imageData)); |
| 5716 | if (!imageBitmap) { |
| 5717 | completion(); |
| 5718 | return; |
| 5719 | } |
| 5720 | |
| 5721 | RetainPtr cgImage = imageBitmap->createPlatformImage(WebCore::DontCopyBackingStore); |
| 5722 | if (!cgImage) { |
| 5723 | completion(); |
| 5724 | return; |
| 5725 | } |
| 5726 | |
| 5727 | WebKit::requestBackgroundRemoval(cgImage.get(), [sourceMIMEType, context, completion = WTF::move(completion), weakSelf](CGImageRef result) mutable { |
| 5728 | auto strongSelf = weakSelf.get(); |
| 5729 | if (!strongSelf) { |
| 5730 | completion(); |
| 5731 | return; |
| 5732 | } |
| 5733 | |
| 5734 | if (result) |
| 5735 | strongSelf->_removeBackgroundData = { { context, { result }, sourceMIMEType } }; |
| 5736 | else |
| 5737 | strongSelf->_removeBackgroundData = std::nullopt; |
| 5738 | completion(); |
| 5739 | }); |
| 5740 | }); |
| 5741 | }); |
| 5742 | } |
| 5743 | |
| 5744 | #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| 5745 | |
| 5746 | - (BOOL)_handleTapOverInteractiveControl:(CGPoint)position |
| 5747 | { |
| 5748 | auto *hitButton = dynamic_objc_cast<UIControl>([self hitTest:position withEvent:nilnullptr]); |
| 5749 | if (!hitButton) |
| 5750 | return NO__objc_no; |
| 5751 | |
| 5752 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 5753 | UIButton *analysisButton = [_imageAnalysisInteraction analysisButton]; |
| 5754 | // This has to be a class check instead of a straight equality check because the `analysisButton` |
| 5755 | // isn't in the view hierarchy, so this is the only way to disambiguate this particular button. |
| 5756 | if (analysisButton && hitButton.class == analysisButton.class) |
| 5757 | [_imageAnalysisInteraction setHighlightSelectableItems:![_imageAnalysisInteraction highlightSelectableItems]]; |
| 5758 | #endif |
| 5759 | |
| 5760 | [hitButton sendActionsForControlEvents:UIControlEventTouchUpInside]; |
| 5761 | |
| 5762 | return YES__objc_yes; |
| 5763 | } |
| 5764 | |
| 5765 | static void logTextInteraction(const char* methodName, UIGestureRecognizer *loupeGestureRecognizer, std::optional<CGPoint> location = std::nullopt, std::optional<UIGestureRecognizerState> gestureState = std::nullopt, std::optional<WKBEGestureTypeBEGestureType> gestureType = std::nullopt, std::optional<WKBESelectionTouchPhaseBESelectionTouchPhase> selectionTouch = std::nullopt, std::optional<WKBESelectionFlagsBESelectionFlags> selectionFlags = std::nullopt) |
| 5766 | { |
| 5767 | TextStream selectionChangeStream(TextStream::LineMode::SingleLine); |
| 5768 | selectionChangeStream << "loupeGestureState=" << toGestureRecognizerState(loupeGestureRecognizer.state); |
| 5769 | if (location) |
| 5770 | selectionChangeStream << ", location={" << location->x << ", " << location->y << "}"; |
| 5771 | |
| 5772 | if (gestureState) |
| 5773 | selectionChangeStream << ", " << "gestureState=" << toGestureRecognizerState(*gestureState); |
| 5774 | |
| 5775 | if (gestureType) |
| 5776 | selectionChangeStream << ", " << "gestureType=" << toGestureType(*gestureType); |
| 5777 | |
| 5778 | if (selectionTouch) |
| 5779 | selectionChangeStream << ", " << "selectionTouch=" << toSelectionTouch(*selectionTouch); |
| 5780 | |
| 5781 | if (selectionFlags) |
| 5782 | selectionChangeStream << ", " << "selectionFlags=" << toSelectionFlags(*selectionFlags); |
| 5783 | |
| 5784 | RELEASE_LOG(TextInteraction, "Text interaction changing selection using '%s' (%s).", methodName, selectionChangeStream.release().utf8().data())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Text interaction changing selection using '%s' (%s)."), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG181" ) = "Text interaction changing selection using '%s' (%s)."; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Text interaction changing selection using '%s' (%s).", methodName , selectionChangeStream.release().utf8().data())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Text interaction changing selection using '%s' (%s)." , methodName, selectionChangeStream.release().utf8().data()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } } ) clang diagnostic pop ; |
| 5785 | } |
| 5786 | |
| 5787 | - (void)selectPositionAtPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler |
| 5788 | { |
| 5789 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5790 | [self _selectPositionAtPoint:point stayingWithinFocusedElement:self._hasFocusedElement completionHandler:completionHandler]; |
| 5791 | } |
| 5792 | |
| 5793 | - (void)_selectPositionAtPoint:(CGPoint)point stayingWithinFocusedElement:(BOOL)stayingWithinFocusedElement completionHandler:(void (^)(void))completionHandler |
| 5794 | { |
| 5795 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point); |
| 5796 | |
| 5797 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5798 | _usingGestureForSelection = YES__objc_yes; |
| 5799 | |
| 5800 | protect(_page)->selectPositionAtPoint(WebCore::IntPoint(point), stayingWithinFocusedElement, [view = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)]() { |
| 5801 | completionHandler(); |
| 5802 | view->_usingGestureForSelection = NO__objc_no; |
| 5803 | }); |
| 5804 | } |
| 5805 | |
| 5806 | - (void)selectPositionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction fromPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler |
| 5807 | { |
| 5808 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point); |
| 5809 | |
| 5810 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5811 | _usingGestureForSelection = YES__objc_yes; |
| 5812 | protect(_page)->selectPositionAtBoundaryWithDirection(WebCore::IntPoint(point), toWKTextGranularity(granularity), toWKSelectionDirection(direction), self._hasFocusedElement, [view = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)]() { |
| 5813 | completionHandler(); |
| 5814 | view->_usingGestureForSelection = NO__objc_no; |
| 5815 | }); |
| 5816 | } |
| 5817 | |
| 5818 | - (void)moveSelectionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction completionHandler:(void (^)(void))completionHandler |
| 5819 | { |
| 5820 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer); |
| 5821 | |
| 5822 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5823 | _usingGestureForSelection = YES__objc_yes; |
| 5824 | protect(_page)->moveSelectionAtBoundaryWithDirection(toWKTextGranularity(granularity), toWKSelectionDirection(direction), [view = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)] { |
| 5825 | completionHandler(); |
| 5826 | view->_usingGestureForSelection = NO__objc_no; |
| 5827 | }); |
| 5828 | } |
| 5829 | |
| 5830 | - (void)selectTextWithGranularity:(UITextGranularity)granularity atPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler |
| 5831 | { |
| 5832 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point); |
| 5833 | |
| 5834 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5835 | _usingGestureForSelection = YES__objc_yes; |
| 5836 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 5837 | _usingMouseDragForSelection = [_mouseInteraction mouseTouchGestureRecognizer]._wk_hasRecognizedOrEnded; |
| 5838 | #endif |
| 5839 | ++_suppressNonEditableSingleTapTextInteractionCount; |
| 5840 | protect(_page)->selectTextWithGranularityAtPoint(WebCore::IntPoint(point), toWKTextGranularity(granularity), self._hasFocusedElement, [view = retainPtr(self), selectionHandler = makeBlockPtr(completionHandler)] { |
| 5841 | selectionHandler(); |
| 5842 | view->_usingGestureForSelection = NO__objc_no; |
| 5843 | --view->_suppressNonEditableSingleTapTextInteractionCount; |
| 5844 | }); |
| 5845 | } |
| 5846 | |
| 5847 | - (void)beginSelectionInDirection:(UITextDirection)direction completionHandler:(void (^)(BOOL endIsMoving))completionHandler |
| 5848 | { |
| 5849 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer); |
| 5850 | |
| 5851 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5852 | protect(_page)->beginSelectionInDirection(toWKSelectionDirection(direction), [selectionHandler = makeBlockPtr(completionHandler)] (bool endIsMoving) { |
| 5853 | selectionHandler(endIsMoving); |
| 5854 | }); |
| 5855 | } |
| 5856 | |
| 5857 | - (void)updateSelectionWithExtentPoint:(CGPoint)point completionHandler:(void (^)(BOOL endIsMoving))completionHandler |
| 5858 | { |
| 5859 | [self updateSelectionWithExtentPoint:point withBoundary:UITextGranularityCharacter completionHandler:completionHandler]; |
| 5860 | } |
| 5861 | |
| 5862 | - (void)updateSelectionWithExtentPoint:(CGPoint)point withBoundary:(UITextGranularity)granularity completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler |
| 5863 | { |
| 5864 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point); |
| 5865 | |
| 5866 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5867 | |
| 5868 | if (granularity == UITextGranularityCharacter && !_usingMouseDragForSelection) { |
| 5869 | auto triggeredByFloatingCursor = !self.textInteractionLoupeGestureRecognizer._wk_hasRecognizedOrEnded |
| 5870 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 5871 | && ![_mouseInteraction mouseTouchGestureRecognizer]._wk_hasRecognizedOrEnded |
| 5872 | #endif |
| 5873 | && !self.textInteractionTapGestureRecognizer._wk_hasRecognizedOrEnded; |
| 5874 | |
| 5875 | auto respectSelectionAnchor = triggeredByFloatingCursor ? WebKit::RespectSelectionAnchor::Yes : WebKit::RespectSelectionAnchor::No; |
| 5876 | [self updateSelectionWithExtentPoint:point hasFocusedElement:self._hasFocusedElement respectSelectionAnchor:respectSelectionAnchor completionHandler:completionHandler]; |
| 5877 | |
| 5878 | if (!triggeredByFloatingCursor && _page->isAutoscrolling()) { |
| 5879 | _selectionInteractionType = SelectionInteractionType::ExtentPoint; |
| 5880 | _lastSelectionExtentPoint.point = [self convertPoint:point toView:self.webView]; |
| 5881 | _lastSelectionExtentPoint.respectSelectionAnchor = respectSelectionAnchor; |
| 5882 | } |
| 5883 | return; |
| 5884 | } |
| 5885 | |
| 5886 | auto source = _usingMouseDragForSelection ? WebKit::TextInteractionSource::Mouse : WebKit::TextInteractionSource::Touch; |
| 5887 | [self updateSelectionWithExtentPointAndBoundary:point textGranularity:toWKTextGranularity(granularity) textInteractionSource:source completionHandler:completionHandler]; |
| 5888 | |
| 5889 | if (_page->isAutoscrolling()) { |
| 5890 | _selectionInteractionType = SelectionInteractionType::ExtentPointAndBoundary; |
| 5891 | _lastSelectionExtentPointAndBoundary.point = [self convertPoint:point toView:self.webView]; |
| 5892 | _lastSelectionExtentPointAndBoundary.granularity = toWKTextGranularity(granularity); |
| 5893 | _lastSelectionExtentPointAndBoundary.interactionSource = source; |
| 5894 | } |
| 5895 | } |
| 5896 | |
| 5897 | - (void)updateSelectionWithExtentPoint:(CGPoint)point hasFocusedElement:(BOOL)hasFocusedElement respectSelectionAnchor:(WebKit::RespectSelectionAnchor)respectSelectionAnchor completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler |
| 5898 | { |
| 5899 | protect(_page)->updateSelectionWithExtentPoint(WebCore::IntPoint(point), self._hasFocusedElement, respectSelectionAnchor, [completionHandler = makeBlockPtr(completionHandler)](bool endIsMoving) { |
| 5900 | completionHandler(static_cast<BOOL>(endIsMoving)); |
| 5901 | }); |
| 5902 | } |
| 5903 | |
| 5904 | - (void)updateSelectionWithExtentPointAndBoundary:(CGPoint)point textGranularity:(WebCore::TextGranularity)textGranularity textInteractionSource:(WebKit::TextInteractionSource)interactionSource completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler |
| 5905 | { |
| 5906 | ++_suppressNonEditableSingleTapTextInteractionCount; |
| 5907 | protect(_page)->updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint(point), textGranularity, self._hasFocusedElement, interactionSource, [completionHandler = makeBlockPtr(completionHandler), protectedSelf = retainPtr(self)] (bool endIsMoving) { |
| 5908 | completionHandler(static_cast<BOOL>(endIsMoving)); |
| 5909 | --protectedSelf->_suppressNonEditableSingleTapTextInteractionCount; |
| 5910 | }); |
| 5911 | } |
| 5912 | |
| 5913 | - (UTF32Char)_characterInRelationToCaretSelection:(int)amount |
| 5914 | { |
| 5915 | if (self.shouldUseAsyncInteractions) |
| 5916 | return [super _characterInRelationToCaretSelection:amount]; |
| 5917 | |
| 5918 | if (amount == -1 && _lastInsertedCharacterToOverrideCharacterBeforeSelection) |
| 5919 | return *_lastInsertedCharacterToOverrideCharacterBeforeSelection; |
| 5920 | |
| 5921 | auto& state = protect(_page)->editorState(); |
| 5922 | if (!state.isContentEditable || state.selectionType != WebCore::SelectionType::Caret || !state.postLayoutData) |
| 5923 | return 0; |
| 5924 | |
| 5925 | switch (amount) { |
| 5926 | case 0: |
| 5927 | return state.postLayoutData->characterAfterSelection; |
| 5928 | case -1: |
| 5929 | return state.postLayoutData->characterBeforeSelection; |
| 5930 | case -2: |
| 5931 | return state.postLayoutData->twoCharacterBeforeSelection; |
| 5932 | default: |
| 5933 | return 0; |
| 5934 | } |
| 5935 | } |
| 5936 | |
| 5937 | - (BOOL)_selectionAtDocumentStart |
| 5938 | { |
| 5939 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG182" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(5939, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 5940 | |
| 5941 | return self.selectionAtDocumentStart; |
| 5942 | } |
| 5943 | |
| 5944 | - (CGRect)textFirstRect |
| 5945 | { |
| 5946 | auto& editorState = protect(_page)->editorState(); |
| 5947 | if (editorState.hasComposition) { |
| 5948 | if (!editorState.hasVisualData()) |
| 5949 | return CGRectZero; |
| 5950 | auto& markedTextRects = editorState.visualData->markedTextRects; |
| 5951 | return markedTextRects.isEmpty() ? CGRectZero : markedTextRects.first().rect(); |
| 5952 | } |
| 5953 | return _autocorrectionData.textFirstRect; |
| 5954 | } |
| 5955 | |
| 5956 | - (CGRect)textLastRect |
| 5957 | { |
| 5958 | auto& editorState = protect(_page)->editorState(); |
| 5959 | if (editorState.hasComposition) { |
| 5960 | if (!editorState.hasVisualData()) |
| 5961 | return CGRectZero; |
| 5962 | auto& markedTextRects = editorState.visualData->markedTextRects; |
| 5963 | return markedTextRects.isEmpty() ? CGRectZero : markedTextRects.last().rect(); |
| 5964 | } |
| 5965 | return _autocorrectionData.textLastRect; |
| 5966 | } |
| 5967 | |
| 5968 | - (void)willInsertFinalDictationResult |
| 5969 | { |
| 5970 | protect(_page)->willInsertFinalDictationResult(); |
| 5971 | } |
| 5972 | |
| 5973 | - (void)didInsertFinalDictationResult |
| 5974 | { |
| 5975 | protect(_page)->didInsertFinalDictationResult(); |
| 5976 | } |
| 5977 | |
| 5978 | - (void)replaceDictatedText:(NSString*)oldText withText:(NSString *)newText |
| 5979 | { |
| 5980 | if (_isHidingKeyboard && _isChangingFocus) |
| 5981 | return; |
| 5982 | |
| 5983 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 5984 | protect(_page)->replaceDictatedText(oldText, newText); |
| 5985 | } |
| 5986 | |
| 5987 | // The completion handler should pass the rect of the correction text after replacing the input text, or nil if the replacement could not be performed. |
| 5988 | - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input isCandidate:(BOOL)isCandidate withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler |
| 5989 | { |
| 5990 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG183" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(5990, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 5991 | |
| 5992 | [self _internalReplaceText:input withText:correction isCandidate:isCandidate completion:[completionHandler = makeBlockPtr(completionHandler), view = retainPtr(self)](bool wasApplied) { |
| 5993 | completionHandler(wasApplied ? [WKAutocorrectionRects autocorrectionRectsWithFirstCGRect:view->_autocorrectionData.textFirstRect lastCGRect:view->_autocorrectionData.textLastRect] : nilnullptr); |
| 5994 | }]; |
| 5995 | } |
| 5996 | |
| 5997 | - (void)_internalReplaceText:(NSString *)input withText:(NSString *)correction isCandidate:(BOOL)isCandidate completion:(Function<void(bool)>&&)completionHandler |
| 5998 | { |
| 5999 | if ([self _disableAutomaticKeyboardUI]) { |
| 6000 | if (completionHandler) |
| 6001 | completionHandler(false); |
| 6002 | return; |
| 6003 | } |
| 6004 | |
| 6005 | // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed. |
| 6006 | const bool useSyncRequest = true; |
| 6007 | |
| 6008 | if (useSyncRequest) { |
| 6009 | if (completionHandler) |
| 6010 | completionHandler(protect(_page)->applyAutocorrection(correction, input, isCandidate)); |
| 6011 | return; |
| 6012 | } |
| 6013 | |
| 6014 | protect(_page)->applyAutocorrection(correction, input, isCandidate, [view = retainPtr(self), completionHandler = WTF::move(completionHandler)](auto& string) { |
| 6015 | if (completionHandler) |
| 6016 | completionHandler(!string.isNull()); |
| 6017 | }); |
| 6018 | } |
| 6019 | |
| 6020 | - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler |
| 6021 | { |
| 6022 | [self applyAutocorrection:correction toString:input isCandidate:NO__objc_no withCompletionHandler:completionHandler]; |
| 6023 | } |
| 6024 | |
| 6025 | - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input shouldUnderline:(BOOL)shouldUnderline withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler |
| 6026 | { |
| 6027 | [self applyAutocorrection:correction toString:input isCandidate:shouldUnderline withCompletionHandler:completionHandler]; |
| 6028 | } |
| 6029 | |
| 6030 | - (void)_cancelPendingAutocorrectionContextHandler |
| 6031 | { |
| 6032 | if (auto handler = WTF::move(_pendingAutocorrectionContextHandler)) |
| 6033 | handler(WebKit::RequestAutocorrectionContextResult::Empty); |
| 6034 | } |
| 6035 | |
| 6036 | - (void)requestAutocorrectionContextWithCompletionHandler:(void (^)(UIWKAutocorrectionContext *autocorrectionContext))completionHandler |
| 6037 | { |
| 6038 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG184" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(6038, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 6039 | |
| 6040 | if (!completionHandler) { |
| 6041 | [NSException raise:NSInvalidArgumentException format:@"Expected a nonnull completion handler in %s.", __PRETTY_FUNCTION__]; |
| 6042 | return; |
| 6043 | } |
| 6044 | |
| 6045 | [self _internalRequestAutocorrectionContextWithCompletionHandler:[completionHandler = makeBlockPtr(completionHandler), strongSelf = retainPtr(self)](WebKit::RequestAutocorrectionContextResult result) { |
| 6046 | switch (result) { |
| 6047 | case WebKit::RequestAutocorrectionContextResult::Empty: |
| 6048 | completionHandler(WKAutocorrectionContext.emptyAutocorrectionContext); |
| 6049 | break; |
| 6050 | case WebKit::RequestAutocorrectionContextResult::LastContext: |
| 6051 | completionHandler([WKAutocorrectionContext autocorrectionContextWithWebContext:strongSelf->_lastAutocorrectionContext]); |
| 6052 | break; |
| 6053 | default: |
| 6054 | ASSERT_NOT_REACHED()((void)0); |
| 6055 | break; |
| 6056 | } |
| 6057 | }]; |
| 6058 | } |
| 6059 | |
| 6060 | - (void)_internalRequestAutocorrectionContextWithCompletionHandler:(CompletionHandler<void(WebKit::RequestAutocorrectionContextResult)>&&)completionHandler |
| 6061 | { |
| 6062 | Ref page = *_page; |
| 6063 | // FIXME: We can remove this code and unconditionally ignore autocorrection context requests when |
| 6064 | // out-of-process keyboard is enabled, once rdar://111936621 is addressed; we'll also be able to |
| 6065 | // remove `EditorState::PostLayoutData::hasGrammarDocumentMarkers` then. |
| 6066 | bool requiresContextToShowGrammarCheckingResults = [&] { |
| 6067 | auto& state = page->editorState(); |
| 6068 | return state.hasPostLayoutData() && state.postLayoutData->hasGrammarDocumentMarkers; |
| 6069 | }(); |
| 6070 | |
| 6071 | if (UIKeyboard.usesInputSystemUI && !requiresContextToShowGrammarCheckingResults) |
| 6072 | return completionHandler(WebKit::RequestAutocorrectionContextResult::Empty); |
| 6073 | |
| 6074 | if (self._disableAutomaticKeyboardUI) |
| 6075 | return completionHandler(WebKit::RequestAutocorrectionContextResult::Empty); |
| 6076 | |
| 6077 | if (!page->hasRunningProcess()) |
| 6078 | return completionHandler(WebKit::RequestAutocorrectionContextResult::Empty); |
| 6079 | |
| 6080 | bool respondWithLastKnownAutocorrectionContext = ([&] { |
| 6081 | if (page->shouldAvoidSynchronouslyWaitingToPreventDeadlock()) |
| 6082 | return true; |
| 6083 | |
| 6084 | if (_domPasteRequestHandler) |
| 6085 | return true; |
| 6086 | |
| 6087 | if (_isUnsuppressingSoftwareKeyboardUsingLastAutocorrectionContext) |
| 6088 | return true; |
| 6089 | |
| 6090 | return !_autocorrectionContextNeedsUpdate; |
| 6091 | })(); |
| 6092 | |
| 6093 | if (respondWithLastKnownAutocorrectionContext) |
| 6094 | return completionHandler(WebKit::RequestAutocorrectionContextResult::LastContext); |
| 6095 | |
| 6096 | if (_pendingAutocorrectionContextHandler) |
| 6097 | return completionHandler(WebKit::RequestAutocorrectionContextResult::Empty); |
| 6098 | |
| 6099 | _pendingAutocorrectionContextHandler = WTF::move(completionHandler); |
| 6100 | protect(_page)->requestAutocorrectionContext(); |
| 6101 | |
| 6102 | if (protect(_page->legacyMainFrameProcess().connection())->waitForAndDispatchImmediately<Messages::WebPageProxy::HandleAutocorrectionContext>(_page->webPageIDInMainFrameProcess(), 1_s, IPC::WaitForOption::DispatchIncomingSyncMessagesWhileWaiting) != IPC::Error::NoError) |
| 6103 | RELEASE_LOG(TextInput, "Timed out while waiting for autocorrection context.")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInput .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Timed out while waiting for autocorrection context."), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG185" ) = "Timed out while waiting for autocorrection context."; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Timed out while waiting for autocorrection context.")]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Timed out while waiting for autocorrection context." ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 6104 | |
| 6105 | if (_autocorrectionContextNeedsUpdate) |
| 6106 | [self _cancelPendingAutocorrectionContextHandler]; |
| 6107 | else if (auto handler = WTF::move(_pendingAutocorrectionContextHandler)) |
| 6108 | handler(WebKit::RequestAutocorrectionContextResult::LastContext); |
| 6109 | } |
| 6110 | |
| 6111 | - (void)_handleAutocorrectionContext:(const WebKit::WebAutocorrectionContext&)context |
| 6112 | { |
| 6113 | _lastAutocorrectionContext = context; |
| 6114 | _autocorrectionContextNeedsUpdate = NO__objc_no; |
| 6115 | [self unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded]; |
| 6116 | } |
| 6117 | |
| 6118 | - (void)updateSoftwareKeyboardSuppressionStateFromWebView |
| 6119 | { |
| 6120 | BOOL webViewIsSuppressingSoftwareKeyboard = [_webView.get() _suppressSoftwareKeyboard]; |
| 6121 | if (webViewIsSuppressingSoftwareKeyboard) { |
| 6122 | _unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate = NO__objc_no; |
| 6123 | self._suppressSoftwareKeyboard = webViewIsSuppressingSoftwareKeyboard; |
| 6124 | return; |
| 6125 | } |
| 6126 | |
| 6127 | if (self._suppressSoftwareKeyboard == webViewIsSuppressingSoftwareKeyboard) |
| 6128 | return; |
| 6129 | |
| 6130 | if (!std::exchange(_unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate, YES__objc_yes)) |
| 6131 | protect(_page)->requestAutocorrectionContext(); |
| 6132 | } |
| 6133 | |
| 6134 | - (void)unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded |
| 6135 | { |
| 6136 | if (!std::exchange(_unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate, NO__objc_no)) |
| 6137 | return; |
| 6138 | |
| 6139 | SetForScope unsuppressSoftwareKeyboardScope { _isUnsuppressingSoftwareKeyboardUsingLastAutocorrectionContext, YES__objc_yes }; |
| 6140 | self._suppressSoftwareKeyboard = NO__objc_no; |
| 6141 | } |
| 6142 | |
| 6143 | - (void)runModalJavaScriptDialog:(CompletionHandler<void()>&&)callback |
| 6144 | { |
| 6145 | if (_isFocusingElementWithKeyboard) |
| 6146 | _pendingRunModalJavaScriptDialogCallback = WTF::move(callback); |
| 6147 | else |
| 6148 | callback(); |
| 6149 | } |
| 6150 | |
| 6151 | - (void)_didStartProvisionalLoadForMainFrame |
| 6152 | { |
| 6153 | // Reset the double tap gesture recognizer to prevent any double click that is in the process of being recognized. |
| 6154 | [_doubleTapGestureRecognizerForDoubleClick _wk_cancel]; |
| 6155 | // We also need to disable the double-tap gesture recognizers that are enabled for double-tap-to-zoom and which |
| 6156 | // are enabled when a single tap is first recognized. This avoids tests running in sequence and simulating taps |
| 6157 | // in the same location to trigger double-tap recognition. |
| 6158 | [self _setDoubleTapGesturesEnabled:NO__objc_no]; |
| 6159 | [_twoFingerDoubleTapGestureRecognizer _wk_cancel]; |
| 6160 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 6161 | [self _cancelImageAnalysis]; |
| 6162 | #endif |
| 6163 | } |
| 6164 | |
| 6165 | - (void)_didCommitLoadForMainFrame |
| 6166 | { |
| 6167 | [protect(_keyboardScrollingAnimator) stopScrollingImmediately]; |
| 6168 | |
| 6169 | _seenHardwareKeyDownInNonEditableElement = NO__objc_no; |
| 6170 | |
| 6171 | [self _elementDidBlur]; |
| 6172 | [self _cancelLongPressGestureRecognizer]; |
| 6173 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 6174 | [self _removeContextMenuHintContainerIfPossible]; |
| 6175 | #endif |
| 6176 | [self _removeContainerForDragPreviews]; |
| 6177 | [self _removeContainerForDropPreviews]; |
| 6178 | [_webView.get() _didCommitLoadForMainFrame]; |
| 6179 | |
| 6180 | _textInteractionDidChangeFocusedElement = NO__objc_no; |
| 6181 | _activeTextInteractionCount = 0; |
| 6182 | _treatAsContentEditableUntilNextEditorStateUpdate = NO__objc_no; |
| 6183 | [self _invalidateCurrentPositionInformation]; |
| 6184 | |
| 6185 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 6186 | [self uninstallImageAnalysisInteraction]; |
| 6187 | #endif |
| 6188 | |
| 6189 | [_textInteractionWrapper reset]; |
| 6190 | |
| 6191 | _keyboardDismissedInCurrentPresentationUpdate = NO__objc_no; |
| 6192 | _editingEndedByUser = YES__objc_yes; |
| 6193 | } |
| 6194 | |
| 6195 | - (void)_nextAccessoryTabForWebView:(id)sender |
| 6196 | { |
| 6197 | [self accessoryTab:YES__objc_yes]; |
| 6198 | } |
| 6199 | |
| 6200 | - (void)_previousAccessoryTabForWebView:(id)sender |
| 6201 | { |
| 6202 | [self accessoryTab:NO__objc_no]; |
| 6203 | } |
| 6204 | |
| 6205 | - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler |
| 6206 | { |
| 6207 | constexpr bool isKeyboardEventValid = false; |
| 6208 | protect(_page)->setInitialFocus(selectingForward, isKeyboardEventValid, { }, [protectedSelf = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)] { |
| 6209 | if (completionHandler) |
| 6210 | completionHandler([protectedSelf becomeFirstResponder]); |
| 6211 | }); |
| 6212 | } |
| 6213 | |
| 6214 | - (WebCore::Color)_tapHighlightColorForFastClick:(BOOL)forFastClick |
| 6215 | { |
| 6216 | ASSERT(_showDebugTapHighlightsForFastClicking)((void)0); |
| 6217 | return forFastClick ? WebCore::SRGBA<uint8_t> { 0, 225, 0, 127 } : WebCore::SRGBA<uint8_t> { 225, 0, 0, 127 }; |
| 6218 | } |
| 6219 | |
| 6220 | - (void)_setDoubleTapGesturesEnabled:(BOOL)enabled |
| 6221 | { |
| 6222 | if (enabled && ![_doubleTapGestureRecognizer isEnabled]) { |
| 6223 | // The first tap recognized after re-enabling double tap gestures will not wait for the |
| 6224 | // second tap before committing. To fix this, we use a new double tap gesture recognizer. |
| 6225 | [self _createAndConfigureDoubleTapGestureRecognizer]; |
| 6226 | } |
| 6227 | |
| 6228 | if (_showDebugTapHighlightsForFastClicking && !enabled) |
| 6229 | _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:YES__objc_yes]; |
| 6230 | |
| 6231 | _doubleTapGesturesAreDisabledTemporarilyForFastTap = !enabled; |
| 6232 | [self _updateDoubleTapGestureRecognizerEnablement]; |
| 6233 | } |
| 6234 | |
| 6235 | - (void)_updateDoubleTapGestureRecognizerEnablement |
| 6236 | { |
| 6237 | [_doubleTapGestureRecognizer setEnabled:!_doubleTapGesturesAreDisabledTemporarilyForFastTap && [_webView.get() _allowsMagnification]]; |
| 6238 | [_nonBlockingDoubleTapGestureRecognizer setEnabled:_doubleTapGesturesAreDisabledTemporarilyForFastTap && [_webView.get() _allowsMagnification]]; |
| 6239 | [self _resetIsDoubleTapPending]; |
| 6240 | } |
| 6241 | |
| 6242 | // MARK: WKFormAccessoryViewDelegate protocol and accessory methods |
| 6243 | |
| 6244 | - (void)accessoryDone |
| 6245 | { |
| 6246 | [self accessoryViewDone:_formAccessoryView.get()]; |
| 6247 | } |
| 6248 | |
| 6249 | - (void)accessoryViewDone:(WKFormAccessoryView *)view |
| 6250 | { |
| 6251 | [_webView.get() _resetFocusPreservationCountAndReleaseActiveFocusState]; |
| 6252 | [self stopRelinquishingFirstResponderToFocusedElement]; |
| 6253 | [self endEditingAndUpdateFocusAppearanceWithReason:EndEditingReasonAccessoryDone]; |
| 6254 | protect(_page)->setIsShowingInputViewForFocusedElement(false); |
| 6255 | } |
| 6256 | |
| 6257 | - (void)updateFocusedElementValue:(NSString *)value |
| 6258 | { |
| 6259 | protect(_page)->setFocusedElementValue(_focusedElementInformation.elementContext, value); |
| 6260 | _focusedElementInformation.value = value; |
| 6261 | } |
| 6262 | |
| 6263 | - (void)updateFocusedElementValueAsColor:(UIColor *)value |
| 6264 | { |
| 6265 | auto color = [&] { |
| 6266 | if (protect(_page->preferences())->inputTypeColorEnhancementsEnabled()) |
| 6267 | return WebCore::Color::createAndPreserveColorSpace(protect(protect(value).get().CGColor)); |
| 6268 | return WebCore::Color(WebCore::roundAndClampToSRGBALossy(protect(protect(value).get().CGColor))); |
| 6269 | }(); |
| 6270 | auto valueAsString = WebCore::serializationForHTML(color); |
| 6271 | |
| 6272 | protect(_page)->setFocusedElementValue(_focusedElementInformation.elementContext, valueAsString); |
| 6273 | _focusedElementInformation.value = valueAsString; |
| 6274 | _focusedElementInformation.colorValue = color; |
| 6275 | } |
| 6276 | |
| 6277 | - (void)updateFocusedElementSelectedIndex:(uint32_t)index allowsMultipleSelection:(bool)allowsMultipleSelection |
| 6278 | { |
| 6279 | protect(_page)->setFocusedElementSelectedIndex(_focusedElementInformation.elementContext, index, allowsMultipleSelection); |
| 6280 | } |
| 6281 | |
| 6282 | - (void)updateFocusedElementFocusedWithDataListDropdown:(BOOL)value |
| 6283 | { |
| 6284 | _focusedElementInformation.isFocusingWithDataListDropdown = value; |
| 6285 | [self reloadInputViews]; |
| 6286 | } |
| 6287 | |
| 6288 | - (void)accessoryTab:(BOOL)isNext |
| 6289 | { |
| 6290 | auto direction = isNext ? WebKit::TabDirection::Next : WebKit::TabDirection::Previous; |
| 6291 | [self accessoryView:_formAccessoryView.get() tabInDirection:direction]; |
| 6292 | } |
| 6293 | |
| 6294 | - (void)accessoryView:(WKFormAccessoryView *)view tabInDirection:(WebKit::TabDirection)direction |
| 6295 | { |
| 6296 | // The input peripheral may need to update the focused DOM node before we switch focus. The UI process does |
| 6297 | // not maintain a handle to the actual focused DOM node – only the web process has such a handle. So, we need |
| 6298 | // to end the editing session now before we tell the web process to switch focus. Once the web process tells |
| 6299 | // us the newly focused element we are no longer are in a position to effect the previously focused element. |
| 6300 | // See <https://bugs.webkit.org/show_bug.cgi?id=134409>. |
| 6301 | [self _endEditing]; |
| 6302 | _inputPeripheral = nilnullptr; // Nullify so that we don't tell the input peripheral to end editing again in -_elementDidBlur. |
| 6303 | |
| 6304 | _isChangingFocusUsingAccessoryTab = YES__objc_yes; |
| 6305 | [self _internalBeginSelectionChange]; |
| 6306 | protect(_page)->focusNextFocusedElement(direction == WebKit::TabDirection::Next, [protectedSelf = retainPtr(self)] { |
| 6307 | [protectedSelf _internalEndSelectionChange]; |
| 6308 | [protectedSelf reloadInputViews]; |
| 6309 | protectedSelf->_isChangingFocusUsingAccessoryTab = NO__objc_no; |
| 6310 | }); |
| 6311 | } |
| 6312 | |
| 6313 | - (void)accessoryViewAutoFill:(WKFormAccessoryView *)view |
| 6314 | { |
| 6315 | RetainPtr inputDelegate = [_webView.get() _inputDelegate]; |
| 6316 | if ([inputDelegate respondsToSelector:@selector(_webView:accessoryViewCustomButtonTappedInFormInputSession:)]) |
| 6317 | [inputDelegate _webView:self.webView accessoryViewCustomButtonTappedInFormInputSession:_formInputSession.get()]; |
| 6318 | } |
| 6319 | |
| 6320 | - (WKFormAccessoryView *)formAccessoryView |
| 6321 | { |
| 6322 | #if PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV ) |
| 6323 | return nilnullptr; |
| 6324 | #else |
| 6325 | #if HAVE(UI_USER_INTERFACE_IDIOM_VISION)(defined 1 && 1) |
| 6326 | if (self.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomVision) |
| 6327 | return nilnullptr; |
| 6328 | #endif |
| 6329 | |
| 6330 | if (!_formAccessoryView) |
| 6331 | _formAccessoryView = adoptNS([[WKFormAccessoryView alloc] initWithInputAssistantItem:self.inputAssistantItem delegate:self]); |
| 6332 | |
| 6333 | return _formAccessoryView.get(); |
| 6334 | #endif |
| 6335 | } |
| 6336 | |
| 6337 | - (void)accessoryOpen |
| 6338 | { |
| 6339 | if (!_inputPeripheral) |
| 6340 | return; |
| 6341 | [self _zoomToRevealFocusedElement]; |
| 6342 | [self _updateAccessory]; |
| 6343 | [_inputPeripheral beginEditing]; |
| 6344 | } |
| 6345 | |
| 6346 | - (void)_updateAccessory |
| 6347 | { |
| 6348 | if (_isUpdatingAccessoryView) |
| 6349 | return; |
| 6350 | |
| 6351 | SetForScope updateAccessoryScope { _isUpdatingAccessoryView, YES__objc_yes }; |
| 6352 | |
| 6353 | auto* accessoryView = self.formAccessoryView; // Creates one, if needed. |
| 6354 | |
| 6355 | if ([accessoryView respondsToSelector:@selector(setNextPreviousItemsVisible:)]) |
| 6356 | [accessoryView setNextPreviousItemsVisible:!self.webView._editable]; |
| 6357 | |
| 6358 | [accessoryView setNextEnabled:_focusedElementInformation.hasNextNode]; |
| 6359 | [accessoryView setPreviousEnabled:_focusedElementInformation.hasPreviousNode]; |
| 6360 | } |
| 6361 | |
| 6362 | // MARK: Keyboard interaction |
| 6363 | // UITextInput protocol implementation |
| 6364 | |
| 6365 | - (BOOL)_allowAnimatedUpdateSelectionRectViews |
| 6366 | { |
| 6367 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG186" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(6367, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 6368 | |
| 6369 | return NO__objc_no; |
| 6370 | } |
| 6371 | |
| 6372 | - (void)beginSelectionChange |
| 6373 | { |
| 6374 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG187" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(6374, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 6375 | |
| 6376 | [self _internalBeginSelectionChange]; |
| 6377 | } |
| 6378 | |
| 6379 | - (void)_internalBeginSelectionChange |
| 6380 | { |
| 6381 | [self _updateInternalStateBeforeSelectionChange]; |
| 6382 | |
| 6383 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 6384 | if (self.shouldUseAsyncInteractions) { |
| 6385 | [protect(_asyncInputDelegate) selectionWillChangeForTextInput:self.asBETextInput]; |
| 6386 | return; |
| 6387 | } |
| 6388 | #endif |
| 6389 | |
| 6390 | [self.inputDelegate selectionWillChange:self]; |
| 6391 | } |
| 6392 | |
| 6393 | - (void)_updateInternalStateBeforeSelectionChange |
| 6394 | { |
| 6395 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 6396 | _selectionChangeNestingLevel++; |
| 6397 | } |
| 6398 | |
| 6399 | - (void)endSelectionChange |
| 6400 | { |
| 6401 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG188" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(6401, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 6402 | |
| 6403 | [self _internalEndSelectionChange]; |
| 6404 | } |
| 6405 | |
| 6406 | - (void)_internalEndSelectionChange |
| 6407 | { |
| 6408 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 6409 | if (self.shouldUseAsyncInteractions) |
| 6410 | [protect(_asyncInputDelegate) selectionDidChangeForTextInput:self.asBETextInput]; |
| 6411 | else |
| 6412 | #endif // USE(BROWSERENGINEKIT) |
| 6413 | [self.inputDelegate selectionDidChange:self]; |
| 6414 | |
| 6415 | [self _updateInternalStateAfterSelectionChange]; |
| 6416 | } |
| 6417 | |
| 6418 | - (void)_updateInternalStateAfterSelectionChange |
| 6419 | { |
| 6420 | if (_selectionChangeNestingLevel) { |
| 6421 | // FIXME (228083): Some layout tests end up triggering unbalanced calls to -endSelectionChange. |
| 6422 | // We should investigate why this happens, (ideally) prevent it from happening, and then assert |
| 6423 | // that `_selectionChangeNestingLevel` is nonzero when calling -endSelectionChange. |
| 6424 | _selectionChangeNestingLevel--; |
| 6425 | } |
| 6426 | } |
| 6427 | |
| 6428 | - (void)willFinishIgnoringCalloutBarFadeAfterPerformingAction |
| 6429 | { |
| 6430 | _ignoreSelectionCommandFadeCount++; |
| 6431 | protect(_page)->scheduleFullEditorStateUpdate(); |
| 6432 | protect(_page)->callAfterNextPresentationUpdate([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 6433 | if (auto strongSelf = weakSelf.get()) |
| 6434 | strongSelf->_ignoreSelectionCommandFadeCount--; |
| 6435 | }); |
| 6436 | } |
| 6437 | |
| 6438 | - (void)_didChangeWebViewEditability |
| 6439 | { |
| 6440 | BOOL webViewIsEditable = self.webView._editable; |
| 6441 | RetainPtr formAccessoryView = _formAccessoryView; |
| 6442 | if ([formAccessoryView respondsToSelector:@selector(setNextPreviousItemsVisible:)]) |
| 6443 | [formAccessoryView setNextPreviousItemsVisible:!webViewIsEditable]; |
| 6444 | |
| 6445 | [_twoFingerSingleTapGestureRecognizer setEnabled:!webViewIsEditable]; |
| 6446 | |
| 6447 | if (webViewIsEditable) { |
| 6448 | static dispatch_once_t onceToken; |
| 6449 | dispatch_once_dispatch_once(&onceToken, ^{ |
| 6450 | class_addProtocol(self.class, @protocol(UITextInputMultiDocument)); |
| 6451 | }); |
| 6452 | } |
| 6453 | } |
| 6454 | |
| 6455 | - (void)insertTextSuggestion:(id)textSuggestion |
| 6456 | { |
| 6457 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 6458 | |
| 6459 | Ref page = *_page; |
| 6460 | if (RetainPtr autoFillSuggestion = dynamic_objc_cast<UITextAutofillSuggestion>(textSuggestion)) { |
| 6461 | // Maintain binary compatibility with UITextAutofillSuggestion, even when using the ServiceExtensions text input. |
| 6462 | page->autofillLoginCredentials([autoFillSuggestion username], [autoFillSuggestion password]); |
| 6463 | return; |
| 6464 | } |
| 6465 | |
| 6466 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 6467 | if (RetainPtr autoFillSuggestion = dynamic_objc_cast<BEAutoFillTextSuggestion>(textSuggestion)) { |
| 6468 | RetainPtr contents = [autoFillSuggestion contents]; |
| 6469 | page->autofillLoginCredentials([contents objectForKey:UITextContentTypeUsername], [contents objectForKey:UITextContentTypePassword]); |
| 6470 | return; |
| 6471 | } |
| 6472 | #endif |
| 6473 | |
| 6474 | if ([_dataListTextSuggestions count] && ![[_formInputSession suggestions] count]) { |
| 6475 | RetainPtr inputText = [textSuggestion inputText]; |
| 6476 | for (WKBETextSuggestionBETextSuggestion *dataListTextSuggestion in _dataListTextSuggestions.get()) { |
| 6477 | if ([inputText isEqualToString:dataListTextSuggestion.inputText]) { |
| 6478 | page->setFocusedElementValue(_focusedElementInformation.elementContext, inputText.get()); |
| 6479 | return; |
| 6480 | } |
| 6481 | } |
| 6482 | } |
| 6483 | |
| 6484 | RetainPtr inputDelegate = [_webView.get() _inputDelegate]; |
| 6485 | if ([inputDelegate respondsToSelector:@selector(_webView:insertTextSuggestion:inInputSession:)]) { |
| 6486 | auto uiTextSuggestion = [&]() -> RetainPtr<UITextSuggestion> { |
| 6487 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 6488 | if (auto beTextSuggestion = dynamic_objc_cast<BETextSuggestion>(textSuggestion)) |
| 6489 | return beTextSuggestion._uikitTextSuggestion; |
| 6490 | #endif |
| 6491 | return textSuggestion; |
| 6492 | }(); |
| 6493 | [inputDelegate _webView:self.webView insertTextSuggestion:uiTextSuggestion.get() inInputSession:_formInputSession.get()]; |
| 6494 | } |
| 6495 | } |
| 6496 | |
| 6497 | - (NSString *)textInRange:(UITextRange *)range |
| 6498 | { |
| 6499 | if (!_page) |
| 6500 | return nilnullptr; |
| 6501 | |
| 6502 | auto& editorState = protect(_page)->editorState(); |
| 6503 | if (!editorState.hasPostLayoutData()) |
| 6504 | return nilnullptr; |
| 6505 | |
| 6506 | if (self.selectedTextRange == range && editorState.selectionType == WebCore::SelectionType::Range) |
| 6507 | return editorState.postLayoutData->wordAtSelection.createNSString().autorelease(); |
| 6508 | |
| 6509 | if (auto relativeRange = dynamic_objc_cast<WKRelativeTextRange>(range)) |
| 6510 | return textRelativeToSelectionStart(relativeRange, *editorState.postLayoutData, _lastInsertedCharacterToOverrideCharacterBeforeSelection).autorelease(); |
| 6511 | |
| 6512 | return nilnullptr; |
| 6513 | } |
| 6514 | |
| 6515 | - (void)replaceRange:(UITextRange *)range withText:(NSString *)text |
| 6516 | { |
| 6517 | } |
| 6518 | |
| 6519 | - (NSArray<WKTextSelectionRect *> *)_textSelectionRects:(const Vector<WebCore::SelectionGeometry>&)rects |
| 6520 | { |
| 6521 | return createNSArray(rects, [&](auto& rect) { |
| 6522 | return adoptNS([[WKTextSelectionRect alloc] initWithSelectionGeometry:rect delegate:self]); |
| 6523 | }).autorelease(); |
| 6524 | } |
| 6525 | |
| 6526 | - (CGFloat)scaleFactorForSelectionRect:(WKTextSelectionRect *)rect |
| 6527 | { |
| 6528 | return self._contentZoomScale; |
| 6529 | } |
| 6530 | |
| 6531 | - (WebCore::FloatQuad)selectionRect:(WKTextSelectionRect *)rect convertQuadToSelectionContainer:(const WebCore::FloatQuad&)quad |
| 6532 | { |
| 6533 | return [self _wk_convertQuad:quad toCoordinateSpace:[self _selectionContainerViewInternal]]; |
| 6534 | } |
| 6535 | |
| 6536 | - (UITextRange *)selectedTextRange |
| 6537 | { |
| 6538 | Ref page = *_page; |
| 6539 | auto& editorState = page->editorState(); |
| 6540 | auto hasSelection = editorState.selectionType != WebCore::SelectionType::None; |
| 6541 | if (!hasSelection || !editorState.hasPostLayoutAndVisualData()) |
| 6542 | return nilnullptr; |
| 6543 | |
| 6544 | auto isRange = editorState.selectionType == WebCore::SelectionType::Range; |
| 6545 | auto isContentEditable = editorState.isContentEditable; |
| 6546 | // UIKit does not expect caret selections in non-editable content. |
| 6547 | if (!isContentEditable && !isRange) |
| 6548 | return nilnullptr; |
| 6549 | |
| 6550 | if (_cachedSelectedTextRange) |
| 6551 | return _cachedSelectedTextRange.get(); |
| 6552 | |
| 6553 | auto caretStartRect = page->editorState().visualData->caretRectAtStart; |
| 6554 | auto caretEndRect = page->editorState().visualData->caretRectAtEnd; |
| 6555 | auto selectionRects = [self _textSelectionRects:page->editorState().visualData->selectionGeometries]; |
| 6556 | auto selectedTextLength = editorState.postLayoutData->selectedTextLength; |
| 6557 | |
| 6558 | _cachedSelectedTextRange = [WKTextRange textRangeWithState:!hasSelection isRange:isRange isEditable:isContentEditable startRect:caretStartRect endRect:caretEndRect selectionRects:selectionRects selectedTextLength:selectedTextLength]; |
| 6559 | return _cachedSelectedTextRange.get(); |
| 6560 | } |
| 6561 | |
| 6562 | - (CGRect)caretRectForPosition:(UITextPosition *)position |
| 6563 | { |
| 6564 | return dynamic_objc_cast<WKTextPosition>(position).positionRect; |
| 6565 | } |
| 6566 | |
| 6567 | - (NSArray *)selectionRectsForRange:(UITextRange *)range |
| 6568 | { |
| 6569 | return dynamic_objc_cast<WKTextRange>(range).selectionRects; |
| 6570 | } |
| 6571 | |
| 6572 | - (void)setSelectedTextRange:(UITextRange *)range |
| 6573 | { |
| 6574 | if (range) |
| 6575 | return; |
| 6576 | #if !PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 6577 | if (!self._hasFocusedElement) |
| 6578 | return; |
| 6579 | #endif |
| 6580 | [self _internalClearSelection]; |
| 6581 | } |
| 6582 | |
| 6583 | - (BOOL)hasMarkedText |
| 6584 | { |
| 6585 | return [_markedText length]; |
| 6586 | } |
| 6587 | |
| 6588 | - (NSString *)markedText |
| 6589 | { |
| 6590 | return _markedText.get(); |
| 6591 | } |
| 6592 | |
| 6593 | - (UITextRange *)markedTextRange |
| 6594 | { |
| 6595 | auto& editorState = protect(_page)->editorState(); |
| 6596 | bool hasComposition = editorState.hasComposition; |
| 6597 | if (!hasComposition || !editorState.hasPostLayoutAndVisualData()) |
| 6598 | return nilnullptr; |
| 6599 | auto& postLayoutData = *editorState.postLayoutData; |
| 6600 | auto& visualData = *editorState.visualData; |
| 6601 | auto caretStartRect = visualData.markedTextCaretRectAtStart; |
| 6602 | auto caretEndRect = visualData.markedTextCaretRectAtEnd; |
| 6603 | auto isRange = caretStartRect != caretEndRect; |
| 6604 | auto isContentEditable = editorState.isContentEditable; |
| 6605 | auto selectionRects = [self _textSelectionRects:visualData.markedTextRects]; |
| 6606 | auto selectedTextLength = postLayoutData.markedText.length(); |
| 6607 | return [WKTextRange textRangeWithState:!hasComposition isRange:isRange isEditable:isContentEditable startRect:caretStartRect endRect:caretEndRect selectionRects:selectionRects selectedTextLength:selectedTextLength]; |
| 6608 | } |
| 6609 | |
| 6610 | - (NSDictionary *)markedTextStyle |
| 6611 | { |
| 6612 | return nilnullptr; |
| 6613 | } |
| 6614 | |
| 6615 | - (void)setMarkedTextStyle:(NSDictionary *)styleDictionary |
| 6616 | { |
| 6617 | } |
| 6618 | |
| 6619 | #if HAVE(REDESIGNED_TEXT_CURSOR)(defined 1 && 1) |
| 6620 | static BOOL shouldUseHighlightsForMarkedText(NSAttributedString *string) |
| 6621 | { |
| 6622 | __block__attribute__((__blocks__(byref))) BOOL result = NO__objc_no; |
| 6623 | |
| 6624 | [string enumerateAttributesInRange:NSMakeRange(0, string.length) options:0 usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attributes, NSRange, BOOL *stop) { |
| 6625 | BOOL hasUnderlineStyle = !![attributes objectForKey:NSUnderlineStyleAttributeNamegetNSUnderlineStyleAttributeNameSingleton()]; |
| 6626 | BOOL hasUnderlineColor = !![attributes objectForKey:NSUnderlineColorAttributeName]; |
| 6627 | |
| 6628 | BOOL hasBackgroundColor = !![attributes objectForKey:NSBackgroundColorAttributeNamegetNSBackgroundColorAttributeNameSingleton()]; |
| 6629 | BOOL hasForegroundColor = !![attributes objectForKey:NSForegroundColorAttributeNamegetNSForegroundColorAttributeNameSingleton()]; |
| 6630 | |
| 6631 | // Marked text may be represented either as an underline or a highlight; this mode is dictated |
| 6632 | // by the attributes it has, and therefore having both types of attributes is not allowed. |
| 6633 | ASSERT(!((hasUnderlineStyle || hasUnderlineColor) && (hasBackgroundColor || hasForegroundColor)))((void)0); |
| 6634 | |
| 6635 | if (hasUnderlineStyle || hasUnderlineColor) { |
| 6636 | result = NO__objc_no; |
| 6637 | *stop = YES__objc_yes; |
| 6638 | } else if (hasBackgroundColor || hasForegroundColor) { |
| 6639 | result = YES__objc_yes; |
| 6640 | *stop = YES__objc_yes; |
| 6641 | } |
| 6642 | }]; |
| 6643 | |
| 6644 | return result; |
| 6645 | } |
| 6646 | |
| 6647 | static Vector<WebCore::CompositionUnderline> extractUnderlines(NSAttributedString *string) |
| 6648 | { |
| 6649 | if (!string.length) |
| 6650 | return { }; |
| 6651 | |
| 6652 | Vector<WebCore::CompositionUnderline> underlines; |
| 6653 | [string enumerateAttributesInRange:NSMakeRange(0, string.length) options:0 usingBlock:[&underlines](NSDictionary<NSAttributedStringKey, id> *attributes, NSRange range, BOOL *) { |
| 6654 | underlines.append({ |
| 6655 | static_cast<unsigned>(range.location), |
| 6656 | static_cast<unsigned>(NSMaxRange(range)), |
| 6657 | WebCore::CompositionUnderlineColor::GivenColor, |
| 6658 | WebCore::Color::black, |
| 6659 | [attributes[NSUnderlineStyleAttributeNamegetNSUnderlineStyleAttributeNameSingleton()] isEqual:@(NSUnderlineStyleThick)] || attributes[NSBackgroundColorAttributeNamegetNSBackgroundColorAttributeNameSingleton()] |
| 6660 | }); |
| 6661 | }]; |
| 6662 | |
| 6663 | std::ranges::sort(underlines); |
| 6664 | |
| 6665 | Vector<WebCore::CompositionUnderline> mergedUnderlines; |
| 6666 | if (!underlines.isEmpty()) |
| 6667 | mergedUnderlines.append({ underlines.first().startOffset, underlines.last().endOffset, WebCore::CompositionUnderlineColor::GivenColor, WebCore::Color::black, false }); |
| 6668 | |
| 6669 | for (auto& underline : underlines) { |
| 6670 | if (underline.thick) |
| 6671 | mergedUnderlines.append(underline); |
| 6672 | } |
| 6673 | |
| 6674 | return mergedUnderlines; |
| 6675 | } |
| 6676 | #endif |
| 6677 | |
| 6678 | static Vector<WebCore::CompositionHighlight> compositionHighlights(NSAttributedString *string) |
| 6679 | { |
| 6680 | if (!string.length) |
| 6681 | return { }; |
| 6682 | |
| 6683 | Vector<WebCore::CompositionHighlight> highlights; |
| 6684 | [string enumerateAttributesInRange:NSMakeRange(0, string.length) options:0 usingBlock:[&highlights](NSDictionary<NSAttributedStringKey, id> *attributes, NSRange range, BOOL *) { |
| 6685 | WebCore::Color backgroundHighlightColor { WebCore::CompositionHighlight::defaultCompositionFillColor }; |
| 6686 | if (UIColor *backgroundColor = attributes[NSBackgroundColorAttributeNamegetNSBackgroundColorAttributeNameSingleton()]) |
| 6687 | backgroundHighlightColor = WebCore::colorFromCocoaColor(backgroundColor); |
| 6688 | |
| 6689 | std::optional<WebCore::Color> foregroundHighlightColor; |
| 6690 | if (UIColor *foregroundColor = attributes[NSForegroundColorAttributeNamegetNSForegroundColorAttributeNameSingleton()]) |
| 6691 | foregroundHighlightColor = WebCore::colorFromCocoaColor(foregroundColor); |
| 6692 | |
| 6693 | highlights.append({ static_cast<unsigned>(range.location), static_cast<unsigned>(NSMaxRange(range)), backgroundHighlightColor, foregroundHighlightColor }); |
| 6694 | }]; |
| 6695 | |
| 6696 | std::ranges::sort(highlights); |
| 6697 | |
| 6698 | Vector<WebCore::CompositionHighlight> mergedHighlights; |
| 6699 | mergedHighlights.reserveInitialCapacity(highlights.size()); |
| 6700 | for (auto& highlight : highlights) { |
| 6701 | if (mergedHighlights.isEmpty() || mergedHighlights.last().backgroundColor != highlight.backgroundColor || mergedHighlights.last().foregroundColor != highlight.foregroundColor) |
| 6702 | mergedHighlights.append(highlight); |
| 6703 | else |
| 6704 | mergedHighlights.last().endOffset = highlight.endOffset; |
| 6705 | } |
| 6706 | |
| 6707 | return mergedHighlights; |
| 6708 | } |
| 6709 | |
| 6710 | - (void)setAttributedMarkedText:(NSAttributedString *)markedText selectedRange:(NSRange)selectedRange |
| 6711 | { |
| 6712 | BOOL hasTextCompletion = ^{ |
| 6713 | if (!markedText.length) |
| 6714 | return NO__objc_no; |
| 6715 | |
| 6716 | // UIKit doesn't include the `NSTextCompletionAttributeName`, so the next best way to detect if this method |
| 6717 | // is being used for a text completion is to check if the attributes match these hard-coded ones. |
| 6718 | RetainPtr textCompletionAttributes = @{ |
| 6719 | NSForegroundColorAttributeNamegetNSForegroundColorAttributeNameSingleton() : UIColor.systemGrayColor, |
| 6720 | NSBackgroundColorAttributeNamegetNSBackgroundColorAttributeNameSingleton() : UIColor.clearColor, |
| 6721 | }; |
| 6722 | |
| 6723 | RetainPtr markedTextAttributes = [markedText attributesAtIndex:0 effectiveRange:nilnullptr]; |
| 6724 | |
| 6725 | RetainPtr foregroundColor = [markedTextAttributes objectForKey:NSForegroundColorAttributeNamegetNSForegroundColorAttributeNameSingleton()]; |
| 6726 | if (![foregroundColor isEqual:UIColor.systemGrayColor]) |
| 6727 | return NO__objc_no; |
| 6728 | |
| 6729 | RetainPtr backgroundColor = [markedTextAttributes objectForKey:NSBackgroundColorAttributeNamegetNSBackgroundColorAttributeNameSingleton()]; |
| 6730 | if (![backgroundColor isEqual:UIColor.clearColor]) |
| 6731 | return NO__objc_no; |
| 6732 | |
| 6733 | return YES__objc_yes; |
| 6734 | }(); |
| 6735 | |
| 6736 | if (hasTextCompletion) { |
| 6737 | protect(_page)->setWritingSuggestion([markedText string], { 0, 0 }); |
| 6738 | return; |
| 6739 | } |
| 6740 | |
| 6741 | Vector<WebCore::CompositionUnderline> underlines; |
| 6742 | Vector<WebCore::CompositionHighlight> highlights; |
| 6743 | |
| 6744 | #if HAVE(REDESIGNED_TEXT_CURSOR)(defined 1 && 1) |
| 6745 | if (!shouldUseHighlightsForMarkedText(markedText)) |
| 6746 | underlines = extractUnderlines(markedText); |
| 6747 | else |
| 6748 | #endif |
| 6749 | highlights = compositionHighlights(markedText); |
| 6750 | |
| 6751 | [self _setMarkedText:markedText.string underlines:underlines highlights:highlights selectedRange:selectedRange]; |
| 6752 | } |
| 6753 | |
| 6754 | - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange |
| 6755 | { |
| 6756 | Vector<WebCore::CompositionUnderline> underlines; |
| 6757 | |
| 6758 | #if HAVE(REDESIGNED_TEXT_CURSOR)(defined 1 && 1) |
| 6759 | underlines.append(WebCore::CompositionUnderline(0, [_markedText length], WebCore::CompositionUnderlineColor::GivenColor, WebCore::Color::black, false)); |
| 6760 | #endif |
| 6761 | |
| 6762 | [self _setMarkedText:markedText underlines:underlines highlights:Vector<WebCore::CompositionHighlight> { } selectedRange:selectedRange]; |
| 6763 | } |
| 6764 | |
| 6765 | - (void)_setMarkedText:(NSString *)markedText underlines:(const Vector<WebCore::CompositionUnderline>&)underlines highlights:(const Vector<WebCore::CompositionHighlight>&)highlights selectedRange:(NSRange)selectedRange |
| 6766 | { |
| 6767 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 6768 | _candidateViewNeedsUpdate = !self.hasMarkedText && _isDeferringKeyEventsToInputMethod; |
| 6769 | _markedText = markedText; |
| 6770 | protect(_page)->setCompositionAsync(markedText, underlines, highlights, { }, selectedRange, { }); |
| 6771 | } |
| 6772 | |
| 6773 | - (void)unmarkText |
| 6774 | { |
| 6775 | _isDeferringKeyEventsToInputMethod = NO__objc_no; |
| 6776 | _markedText = nilnullptr; |
| 6777 | protect(_page)->confirmCompositionAsync(); |
| 6778 | } |
| 6779 | |
| 6780 | - (UITextPosition *)beginningOfDocument |
| 6781 | { |
| 6782 | return nilnullptr; |
| 6783 | } |
| 6784 | |
| 6785 | - (UITextPosition *)endOfDocument |
| 6786 | { |
| 6787 | return nilnullptr; |
| 6788 | } |
| 6789 | |
| 6790 | - (BOOL)_isAnchoredToCurrentSelection:(UITextPosition *)position |
| 6791 | { |
| 6792 | return [position isKindOfClass:WKRelativeTextPosition.class] || [position isEqual:self.selectedTextRange.start] || [position isEqual:self.selectedTextRange.end]; |
| 6793 | } |
| 6794 | |
| 6795 | - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition |
| 6796 | { |
| 6797 | if (!self.shouldUseAsyncInteractions) |
| 6798 | return nilnullptr; |
| 6799 | |
| 6800 | if (![self _isAnchoredToCurrentSelection:fromPosition] || ![self _isAnchoredToCurrentSelection:toPosition]) |
| 6801 | return nilnullptr; |
| 6802 | |
| 6803 | return adoptNS([[WKRelativeTextRange alloc] initWithStart:fromPosition end:toPosition]).autorelease(); |
| 6804 | } |
| 6805 | |
| 6806 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset |
| 6807 | { |
| 6808 | if (!self.shouldUseAsyncInteractions) |
| 6809 | return nilnullptr; |
| 6810 | |
| 6811 | if (![self _isAnchoredToCurrentSelection:position]) |
| 6812 | return nilnullptr; |
| 6813 | |
| 6814 | return positionWithOffsetFrom(position, offset); |
| 6815 | } |
| 6816 | |
| 6817 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset |
| 6818 | { |
| 6819 | return nilnullptr; |
| 6820 | } |
| 6821 | |
| 6822 | - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other |
| 6823 | { |
| 6824 | return NSOrderedSame; |
| 6825 | } |
| 6826 | |
| 6827 | - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)to |
| 6828 | { |
| 6829 | #if HAVE(UIFINDINTERACTION)(defined 1 && 1) |
| 6830 | if ([from isKindOfClass:[WKFoundDOMTextPosition class]] && [to isKindOfClass:[WKFoundDOMTextPosition class]]) { |
| 6831 | WKFoundDOMTextPosition *fromPosition = (WKFoundDOMTextPosition *)from; |
| 6832 | WKFoundDOMTextPosition *toPosition = (WKFoundDOMTextPosition *)to; |
| 6833 | |
| 6834 | if (fromPosition.order != toPosition.order) |
| 6835 | return fromPosition.order - toPosition.order; |
| 6836 | |
| 6837 | return fromPosition.offset - toPosition.offset; |
| 6838 | } |
| 6839 | |
| 6840 | if ([from isKindOfClass:[WKFoundPDFTextPosition class]] && [to isKindOfClass:[WKFoundPDFTextPosition class]]) { |
| 6841 | WKFoundPDFTextPosition *fromPosition = (WKFoundPDFTextPosition *)from; |
| 6842 | WKFoundPDFTextPosition *toPosition = (WKFoundPDFTextPosition *)to; |
| 6843 | |
| 6844 | if (fromPosition.order != toPosition.order) |
| 6845 | return fromPosition.order - toPosition.order; |
| 6846 | |
| 6847 | if (fromPosition.page == toPosition.page) |
| 6848 | return fromPosition.offset - toPosition.offset; |
| 6849 | return fromPosition.page - toPosition.page; |
| 6850 | } |
| 6851 | |
| 6852 | if ([from isKindOfClass:[WKFoundTextPosition class]] && [to isKindOfClass:[WKFoundTextPosition class]]) { |
| 6853 | WKFoundTextPosition *fromPosition = (WKFoundTextPosition *)from; |
| 6854 | WKFoundTextPosition *toPosition = (WKFoundTextPosition *)to; |
| 6855 | |
| 6856 | return fromPosition.order - toPosition.order; |
| 6857 | } |
| 6858 | #endif |
| 6859 | |
| 6860 | return 0; |
| 6861 | } |
| 6862 | |
| 6863 | - (id <UITextInputTokenizer>)tokenizer |
| 6864 | { |
| 6865 | return nilnullptr; |
| 6866 | } |
| 6867 | |
| 6868 | - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction |
| 6869 | { |
| 6870 | return nilnullptr; |
| 6871 | } |
| 6872 | |
| 6873 | - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction |
| 6874 | { |
| 6875 | return nilnullptr; |
| 6876 | } |
| 6877 | |
| 6878 | - (NSWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction |
| 6879 | { |
| 6880 | return NSWritingDirectionLeftToRight; |
| 6881 | } |
| 6882 | |
| 6883 | static WebKit::WritingDirection coreWritingDirection(NSWritingDirection direction) |
| 6884 | { |
| 6885 | switch (direction) { |
| 6886 | case NSWritingDirectionNatural: |
| 6887 | return WebCore::WritingDirection::Natural; |
| 6888 | case NSWritingDirectionLeftToRight: |
| 6889 | return WebCore::WritingDirection::LeftToRight; |
| 6890 | case NSWritingDirectionRightToLeft: |
| 6891 | return WebCore::WritingDirection::RightToLeft; |
| 6892 | default: |
| 6893 | ASSERT_NOT_REACHED()((void)0); |
| 6894 | return WebCore::WritingDirection::Natural; |
| 6895 | } |
| 6896 | } |
| 6897 | |
| 6898 | - (void)setBaseWritingDirection:(NSWritingDirection)direction forRange:(UITextRange *)range |
| 6899 | { |
| 6900 | if (!_page->isEditable()) |
| 6901 | return; |
| 6902 | |
| 6903 | if (range && ![range isEqual:self.selectedTextRange]) { |
| 6904 | // We currently only support changing the base writing direction at the selection. |
| 6905 | return; |
| 6906 | } |
| 6907 | protect(_page)->setBaseWritingDirection(coreWritingDirection(direction)); |
| 6908 | } |
| 6909 | |
| 6910 | - (CGRect)firstRectForRange:(UITextRange *)range |
| 6911 | { |
| 6912 | return CGRectZero; |
| 6913 | } |
| 6914 | |
| 6915 | /* Hit testing. */ |
| 6916 | - (UITextPosition *)closestPositionToPoint:(CGPoint)point |
| 6917 | { |
| 6918 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 6919 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point)); |
| 6920 | [self requestAsynchronousPositionInformationUpdate:request]; |
| 6921 | if ([self _currentPositionInformationIsApproximatelyValidForRequest:request radiusForApproximation:2] && _positionInformation.isSelectable()) |
| 6922 | return [WKTextPosition textPositionWithRect:_positionInformation.caretRect]; |
| 6923 | #endif |
| 6924 | return nilnullptr; |
| 6925 | } |
| 6926 | |
| 6927 | - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range |
| 6928 | { |
| 6929 | return nilnullptr; |
| 6930 | } |
| 6931 | |
| 6932 | - (UITextRange *)characterRangeAtPoint:(CGPoint)point |
| 6933 | { |
| 6934 | return nilnullptr; |
| 6935 | } |
| 6936 | |
| 6937 | - (void)deleteBackward |
| 6938 | { |
| 6939 | protect(_page)->executeEditCommand("deleteBackward"_s); |
| 6940 | } |
| 6941 | |
| 6942 | - (BOOL)_shouldSimulateKeyboardInputOnTextInsertion |
| 6943 | { |
| 6944 | #if HAVE(PENCILKIT_TEXT_INPUT)(defined 1 && 1) |
| 6945 | return [_scribbleInteraction isHandlingWriting]; |
| 6946 | #else |
| 6947 | return NO__objc_no; |
| 6948 | #endif |
| 6949 | } |
| 6950 | |
| 6951 | // Inserts the given string, replacing any selected or marked text. |
| 6952 | - (void)insertText:(NSString *)aStringValue |
| 6953 | { |
| 6954 | if (_isHidingKeyboard && _isChangingFocus) |
| 6955 | return; |
| 6956 | |
| 6957 | Ref page = *_page; |
| 6958 | WebKit::InsertTextOptions options; |
| 6959 | options.processingUserGesture = _isHandlingActiveKeyEvent || _isHandlingActivePressesEvent; |
| 6960 | options.shouldSimulateKeyboardInput = [self _shouldSimulateKeyboardInputOnTextInsertion]; |
| 6961 | options.directionFromCurrentInputMode = [self] -> std::optional<WebCore::TextDirection> { |
| 6962 | NSString *inputLanguage = self.textInputMode.primaryLanguage; |
| 6963 | if (!inputLanguage.length) |
| 6964 | return { }; |
| 6965 | |
| 6966 | switch ([NSLocale characterDirectionForLanguage:inputLanguage]) { |
| 6967 | case NSLocaleLanguageDirectionRightToLeft: |
| 6968 | return WebCore::TextDirection::RTL; |
| 6969 | case NSLocaleLanguageDirectionLeftToRight: |
| 6970 | return WebCore::TextDirection::LTR; |
| 6971 | case NSLocaleLanguageDirectionTopToBottom: |
| 6972 | case NSLocaleLanguageDirectionBottomToTop: |
| 6973 | case NSLocaleLanguageDirectionUnknown: |
| 6974 | return { }; |
| 6975 | } |
| 6976 | ASSERT_NOT_REACHED()((void)0); |
| 6977 | return { }; |
| 6978 | }(); |
| 6979 | page->insertTextAsync(aStringValue, { }, WTF::move(options)); |
| 6980 | |
| 6981 | if (_focusedElementInformation.autocapitalizeType == WebCore::AutocapitalizeType::Words && aStringValue.length) { |
| 6982 | _lastInsertedCharacterToOverrideCharacterBeforeSelection = [aStringValue characterAtIndex:aStringValue.length - 1]; |
| 6983 | page->scheduleFullEditorStateUpdate(); |
| 6984 | } |
| 6985 | |
| 6986 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 6987 | } |
| 6988 | |
| 6989 | - (void)insertText:(NSString *)aStringValue alternatives:(NSArray<NSString *> *)alternatives style:(UITextAlternativeStyle)style |
| 6990 | { |
| 6991 | if (!alternatives.count) { |
| 6992 | [self insertText:aStringValue]; |
| 6993 | return; |
| 6994 | } |
| 6995 | |
| 6996 | BOOL isLowConfidence = style == UITextAlternativeStyleLowConfidence; |
| 6997 | auto nsAlternatives = adoptNS([[NSTextAlternatives alloc] initWithPrimaryString:aStringValue alternativeStrings:alternatives isLowConfidence:isLowConfidence]); |
| 6998 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 6999 | auto textAlternatives = adoptNS([[PlatformTextAlternativesBETextAlternatives alloc] _initWithNSTextAlternatives:nsAlternatives.get()]); |
| 7000 | #else |
| 7001 | auto textAlternatives = nsAlternatives; |
| 7002 | #endif |
| 7003 | WebCore::TextAlternativeWithRange textAlternativeWithRange { textAlternatives.get(), NSMakeRange(0, aStringValue.length) }; |
| 7004 | |
| 7005 | WebKit::InsertTextOptions options; |
| 7006 | options.shouldSimulateKeyboardInput = [self _shouldSimulateKeyboardInputOnTextInsertion]; |
| 7007 | protect(_page)->insertDictatedTextAsync(aStringValue, { }, { textAlternativeWithRange }, WTF::move(options)); |
| 7008 | |
| 7009 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 7010 | } |
| 7011 | |
| 7012 | - (BOOL)hasText |
| 7013 | { |
| 7014 | if (_isFocusingElementWithKeyboard || _page->waitingForPostLayoutEditorStateUpdateAfterFocusingElement()) |
| 7015 | return _focusedElementInformation.hasPlainText; |
| 7016 | |
| 7017 | auto& editorState = protect(_page)->editorState(); |
| 7018 | return editorState.hasPostLayoutData() && editorState.postLayoutData->hasPlainText; |
| 7019 | } |
| 7020 | |
| 7021 | // Accepts either NSTextAlternatives, or an equivalent type that's currently defined as PlatformTextAlternatives. |
| 7022 | - (void)addTextAlternatives:(NSObject *)alternatives |
| 7023 | { |
| 7024 | RetainPtr platformAlternatives = dynamic_objc_cast<PlatformTextAlternativesBETextAlternatives>(alternatives); |
| 7025 | |
| 7026 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 7027 | if (!platformAlternatives) { |
| 7028 | if (RetainPtr nsAlternatives = dynamic_objc_cast<NSTextAlternatives>(alternatives)) |
| 7029 | platformAlternatives = adoptNS([[PlatformTextAlternativesBETextAlternatives alloc] _initWithNSTextAlternatives:nsAlternatives.get()]); |
| 7030 | } |
| 7031 | #endif |
| 7032 | |
| 7033 | if (!platformAlternatives) |
| 7034 | return; |
| 7035 | |
| 7036 | protect(_page)->addDictationAlternative({ platformAlternatives.get(), NSMakeRange(0, [platformAlternatives primaryString].length) }); |
| 7037 | } |
| 7038 | |
| 7039 | - (void)removeEmojiAlternatives |
| 7040 | { |
| 7041 | protect(_page)->dictationAlternativesAtSelection([weakSelf = WeakObjCPtr<WKContentView>(self)](auto&& contexts) { |
| 7042 | auto strongSelf = weakSelf.get(); |
| 7043 | if (!strongSelf || !strongSelf->_page) |
| 7044 | return; |
| 7045 | |
| 7046 | RefPtr page = strongSelf->_page; |
| 7047 | Vector<WebCore::DictationContext> contextsToRemove; |
| 7048 | for (auto context : contexts) { |
| 7049 | auto alternatives = protect(page->pageClient())->platformDictationAlternatives(context); |
| 7050 | if (!alternatives) |
| 7051 | continue; |
| 7052 | |
| 7053 | auto originalAlternatives = alternatives.alternativeStrings; |
| 7054 | auto nonEmojiAlternatives = [NSMutableArray arrayWithCapacity:originalAlternatives.count]; |
| 7055 | for (NSString *alternative in originalAlternatives) { |
| 7056 | if (!alternative._containsEmojiOnly) |
| 7057 | [nonEmojiAlternatives addObject:alternative]; |
| 7058 | } |
| 7059 | |
| 7060 | if (nonEmojiAlternatives.count == originalAlternatives.count) |
| 7061 | continue; |
| 7062 | |
| 7063 | RetainPtr<NSTextAlternatives> nsReplacement; |
| 7064 | if (nonEmojiAlternatives.count) |
| 7065 | nsReplacement = adoptNS([[NSTextAlternatives alloc] initWithPrimaryString:alternatives.primaryString alternativeStrings:nonEmojiAlternatives isLowConfidence:alternatives.isLowConfidence]); |
| 7066 | else |
| 7067 | contextsToRemove.append(context); |
| 7068 | |
| 7069 | RetainPtr<PlatformTextAlternativesBETextAlternatives> platformReplacement; |
| 7070 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 7071 | platformReplacement = adoptNS([[PlatformTextAlternativesBETextAlternatives alloc] _initWithNSTextAlternatives:nsReplacement.get()]); |
| 7072 | #else |
| 7073 | platformReplacement = nsReplacement; |
| 7074 | #endif |
| 7075 | if (RefPtr pageClient = page->pageClient()) |
| 7076 | pageClient->replaceDictationAlternatives(platformReplacement.get(), context); |
| 7077 | } |
| 7078 | page->clearDictationAlternatives(WTF::move(contextsToRemove)); |
| 7079 | }); |
| 7080 | } |
| 7081 | |
| 7082 | // end of UITextInput protocol implementation |
| 7083 | |
| 7084 | static UITextAutocapitalizationType toUITextAutocapitalize(WebCore::AutocapitalizeType webkitType) |
| 7085 | { |
| 7086 | switch (webkitType) { |
| 7087 | case WebCore::AutocapitalizeType::Default: |
| 7088 | return UITextAutocapitalizationTypeSentences; |
| 7089 | case WebCore::AutocapitalizeType::None: |
| 7090 | return UITextAutocapitalizationTypeNone; |
| 7091 | case WebCore::AutocapitalizeType::Words: |
| 7092 | return UITextAutocapitalizationTypeWords; |
| 7093 | case WebCore::AutocapitalizeType::Sentences: |
| 7094 | return UITextAutocapitalizationTypeSentences; |
| 7095 | case WebCore::AutocapitalizeType::AllCharacters: |
| 7096 | return UITextAutocapitalizationTypeAllCharacters; |
| 7097 | } |
| 7098 | |
| 7099 | return UITextAutocapitalizationTypeSentences; |
| 7100 | } |
| 7101 | |
| 7102 | - (NSString *)contentTypeFromFieldName:(WebCore::AutofillFieldName)fieldName |
| 7103 | { |
| 7104 | switch (fieldName) { |
| 7105 | case WebCore::AutofillFieldName::Name: |
| 7106 | return UITextContentTypeName; |
| 7107 | case WebCore::AutofillFieldName::HonorificPrefix: |
| 7108 | return UITextContentTypeNamePrefix; |
| 7109 | case WebCore::AutofillFieldName::GivenName: |
| 7110 | return UITextContentTypeGivenName; |
| 7111 | case WebCore::AutofillFieldName::AdditionalName: |
| 7112 | return UITextContentTypeMiddleName; |
| 7113 | case WebCore::AutofillFieldName::FamilyName: |
| 7114 | return UITextContentTypeFamilyName; |
| 7115 | case WebCore::AutofillFieldName::HonorificSuffix: |
| 7116 | return UITextContentTypeNameSuffix; |
| 7117 | case WebCore::AutofillFieldName::Nickname: |
| 7118 | return UITextContentTypeNickname; |
| 7119 | case WebCore::AutofillFieldName::OrganizationTitle: |
| 7120 | return UITextContentTypeJobTitle; |
| 7121 | case WebCore::AutofillFieldName::Organization: |
| 7122 | return UITextContentTypeOrganizationName; |
| 7123 | case WebCore::AutofillFieldName::StreetAddress: |
| 7124 | return UITextContentTypeFullStreetAddress; |
| 7125 | case WebCore::AutofillFieldName::AddressLine1: |
| 7126 | return UITextContentTypeStreetAddressLine1; |
| 7127 | case WebCore::AutofillFieldName::AddressLine2: |
| 7128 | return UITextContentTypeStreetAddressLine2; |
| 7129 | case WebCore::AutofillFieldName::AddressLevel3: |
| 7130 | return UITextContentTypeSublocality; |
| 7131 | case WebCore::AutofillFieldName::AddressLevel2: |
| 7132 | return UITextContentTypeAddressCity; |
| 7133 | case WebCore::AutofillFieldName::AddressLevel1: |
| 7134 | return UITextContentTypeAddressState; |
| 7135 | case WebCore::AutofillFieldName::CountryName: |
| 7136 | return UITextContentTypeCountryName; |
| 7137 | case WebCore::AutofillFieldName::PostalCode: |
| 7138 | return UITextContentTypePostalCode; |
| 7139 | case WebCore::AutofillFieldName::Tel: |
| 7140 | return UITextContentTypeTelephoneNumber; |
| 7141 | case WebCore::AutofillFieldName::Email: |
| 7142 | return UITextContentTypeEmailAddress; |
| 7143 | case WebCore::AutofillFieldName::URL: |
| 7144 | return UITextContentTypeURL; |
| 7145 | case WebCore::AutofillFieldName::Username: |
| 7146 | case WebCore::AutofillFieldName::WebAuthn: |
| 7147 | return UITextContentTypeUsername; |
| 7148 | case WebCore::AutofillFieldName::OneTimeCode: |
| 7149 | return UITextContentTypeOneTimeCode; |
| 7150 | #if HAVE(ESIM_AUTOFILL_SYSTEM_SUPPORT)(defined 1 && 1) |
| 7151 | case WebCore::AutofillFieldName::DeviceEID: |
| 7152 | return protect(_page)->shouldAllowAutoFillForCellularIdentifiers() ? UITextContentTypeCellularEID : nilnullptr; |
| 7153 | case WebCore::AutofillFieldName::DeviceIMEI: |
| 7154 | return protect(_page)->shouldAllowAutoFillForCellularIdentifiers() ? UITextContentTypeCellularIMEI : nilnullptr; |
| 7155 | #else |
| 7156 | case WebCore::AutofillFieldName::DeviceEID: |
| 7157 | case WebCore::AutofillFieldName::DeviceIMEI: |
| 7158 | #endif |
| 7159 | case WebCore::AutofillFieldName::None: |
| 7160 | case WebCore::AutofillFieldName::NewPassword: |
| 7161 | case WebCore::AutofillFieldName::CurrentPassword: |
| 7162 | case WebCore::AutofillFieldName::AddressLine3: |
| 7163 | case WebCore::AutofillFieldName::AddressLevel4: |
| 7164 | case WebCore::AutofillFieldName::Country: |
| 7165 | case WebCore::AutofillFieldName::CcName: |
| 7166 | case WebCore::AutofillFieldName::CcGivenName: |
| 7167 | case WebCore::AutofillFieldName::CcAdditionalName: |
| 7168 | case WebCore::AutofillFieldName::CcFamilyName: |
| 7169 | case WebCore::AutofillFieldName::CcNumber: |
| 7170 | case WebCore::AutofillFieldName::CcExp: |
| 7171 | case WebCore::AutofillFieldName::CcExpMonth: |
| 7172 | case WebCore::AutofillFieldName::CcExpYear: |
| 7173 | case WebCore::AutofillFieldName::CcCsc: |
| 7174 | case WebCore::AutofillFieldName::CcType: |
| 7175 | case WebCore::AutofillFieldName::TransactionCurrency: |
| 7176 | case WebCore::AutofillFieldName::TransactionAmount: |
| 7177 | case WebCore::AutofillFieldName::Language: |
| 7178 | case WebCore::AutofillFieldName::Bday: |
| 7179 | case WebCore::AutofillFieldName::BdayDay: |
| 7180 | case WebCore::AutofillFieldName::BdayMonth: |
| 7181 | case WebCore::AutofillFieldName::BdayYear: |
| 7182 | case WebCore::AutofillFieldName::Sex: |
| 7183 | case WebCore::AutofillFieldName::Photo: |
| 7184 | case WebCore::AutofillFieldName::TelCountryCode: |
| 7185 | case WebCore::AutofillFieldName::TelNational: |
| 7186 | case WebCore::AutofillFieldName::TelAreaCode: |
| 7187 | case WebCore::AutofillFieldName::TelLocal: |
| 7188 | case WebCore::AutofillFieldName::TelLocalPrefix: |
| 7189 | case WebCore::AutofillFieldName::TelLocalSuffix: |
| 7190 | case WebCore::AutofillFieldName::TelExtension: |
| 7191 | case WebCore::AutofillFieldName::Impp: |
| 7192 | break; |
| 7193 | }; |
| 7194 | |
| 7195 | return nilnullptr; |
| 7196 | } |
| 7197 | |
| 7198 | - (BOOL)_requiresLegacyTextInputTraits |
| 7199 | { |
| 7200 | if (_cachedRequiresLegacyTextInputTraits.has_value()) |
| 7201 | return *_cachedRequiresLegacyTextInputTraits; |
| 7202 | |
| 7203 | _cachedRequiresLegacyTextInputTraits = [&]() -> BOOL { |
| 7204 | if (!self.shouldUseAsyncInteractions) |
| 7205 | return YES__objc_yes; |
| 7206 | |
| 7207 | if ([_webView.get() class] == WKWebView.class) |
| 7208 | return NO__objc_no; |
| 7209 | |
| 7210 | static auto defaultTextInputTraitsMethod = class_getMethodImplementation(WKWebView.class, @selector(_textInputTraits)); |
| 7211 | return defaultTextInputTraitsMethod != class_getMethodImplementation([_webView.get() class], @selector(_textInputTraits)); |
| 7212 | }(); |
| 7213 | return *_cachedRequiresLegacyTextInputTraits; |
| 7214 | } |
| 7215 | |
| 7216 | // UITextInputPrivate protocol |
| 7217 | // Direct access to the (private) UITextInputTraits object. |
| 7218 | - (UITextInputTraits *)textInputTraits |
| 7219 | { |
| 7220 | if (!self._requiresLegacyTextInputTraits) |
| 7221 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG189" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7221, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7222 | |
| 7223 | _legacyTextInputTraits = [_webView.get() _textInputTraits]; |
| 7224 | return _legacyTextInputTraits.get(); |
| 7225 | } |
| 7226 | |
| 7227 | - (UITextInputTraits *)textInputTraitsForWebView |
| 7228 | { |
| 7229 | if (!_legacyTextInputTraits) |
| 7230 | _legacyTextInputTraits = adoptNS([UITextInputTraits new]); |
| 7231 | |
| 7232 | // Do not change traits when dismissing the keyboard. |
| 7233 | if (!_isBlurringFocusedElement) |
| 7234 | [self _updateTextInputTraits:_legacyTextInputTraits.get()]; |
| 7235 | |
| 7236 | return _legacyTextInputTraits.get(); |
| 7237 | } |
| 7238 | |
| 7239 | - (void)_updateTextInputTraits:(id<UITextInputTraits>)traits |
| 7240 | { |
| 7241 | traits.secureTextEntry = _focusedElementInformation.elementType == WebKit::InputType::Password || [_formInputSession forceSecureTextEntry]; |
| 7242 | |
| 7243 | switch (_focusedElementInformation.enterKeyHint) { |
| 7244 | case WebCore::EnterKeyHint::Enter: |
| 7245 | traits.returnKeyType = UIReturnKeyDefault; |
| 7246 | break; |
| 7247 | case WebCore::EnterKeyHint::Done: |
| 7248 | traits.returnKeyType = UIReturnKeyDone; |
| 7249 | break; |
| 7250 | case WebCore::EnterKeyHint::Go: |
| 7251 | traits.returnKeyType = UIReturnKeyGo; |
| 7252 | break; |
| 7253 | case WebCore::EnterKeyHint::Next: |
| 7254 | traits.returnKeyType = UIReturnKeyNext; |
| 7255 | break; |
| 7256 | case WebCore::EnterKeyHint::Search: |
| 7257 | traits.returnKeyType = UIReturnKeySearch; |
| 7258 | break; |
| 7259 | case WebCore::EnterKeyHint::Send: |
| 7260 | traits.returnKeyType = UIReturnKeySend; |
| 7261 | break; |
| 7262 | default: { |
| 7263 | if (!_focusedElementInformation.formAction.isEmpty()) |
| 7264 | traits.returnKeyType = _focusedElementInformation.elementType == WebKit::InputType::Search ? UIReturnKeySearch : UIReturnKeyGo; |
| 7265 | } |
| 7266 | } |
| 7267 | |
| 7268 | #if HAVE(UI_CONVERSATION_CONTEXT)(defined 1 && 1) |
| 7269 | if ([traits respondsToSelector:@selector(setConversationContext:)]) |
| 7270 | traits.conversationContext = [_webView.get() conversationContext]; |
| 7271 | #endif |
| 7272 | |
| 7273 | BOOL disableAutocorrectAndAutocapitalize = _focusedElementInformation.elementType == WebKit::InputType::Password || _focusedElementInformation.elementType == WebKit::InputType::Email |
| 7274 | || _focusedElementInformation.elementType == WebKit::InputType::URL || _focusedElementInformation.formAction.contains("login"_s); |
| 7275 | if ([traits respondsToSelector:@selector(setAutocapitalizationType:)]) |
| 7276 | traits.autocapitalizationType = disableAutocorrectAndAutocapitalize ? UITextAutocapitalizationTypeNone : toUITextAutocapitalize(_focusedElementInformation.autocapitalizeType); |
| 7277 | if ([traits respondsToSelector:@selector(setAutocorrectionType:)]) |
| 7278 | traits.autocorrectionType = disableAutocorrectAndAutocapitalize ? UITextAutocorrectionTypeNo : (_focusedElementInformation.isAutocorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo); |
| 7279 | |
| 7280 | if (!_focusedElementInformation.isSpellCheckingEnabled) { |
| 7281 | if ([traits respondsToSelector:@selector(setSmartQuotesType:)]) |
| 7282 | traits.smartQuotesType = UITextSmartQuotesTypeNo; |
| 7283 | if ([traits respondsToSelector:@selector(setSmartDashesType:)]) |
| 7284 | traits.smartDashesType = UITextSmartDashesTypeNo; |
| 7285 | if ([traits respondsToSelector:@selector(setSpellCheckingType:)]) |
| 7286 | traits.spellCheckingType = UITextSpellCheckingTypeNo; |
| 7287 | } |
| 7288 | |
| 7289 | switch (_focusedElementInformation.inputMode) { |
| 7290 | case WebCore::InputMode::None: |
| 7291 | case WebCore::InputMode::Unspecified: |
| 7292 | switch (_focusedElementInformation.elementType) { |
| 7293 | case WebKit::InputType::Phone: |
| 7294 | traits.keyboardType = UIKeyboardTypePhonePad; |
| 7295 | break; |
| 7296 | case WebKit::InputType::URL: |
| 7297 | traits.keyboardType = UIKeyboardTypeURL; |
| 7298 | break; |
| 7299 | case WebKit::InputType::Email: |
| 7300 | traits.keyboardType = UIKeyboardTypeEmailAddress; |
| 7301 | break; |
| 7302 | case WebKit::InputType::Number: |
| 7303 | traits.keyboardType = UIKeyboardTypeNumbersAndPunctuation; |
| 7304 | break; |
| 7305 | case WebKit::InputType::NumberPad: |
| 7306 | traits.keyboardType = UIKeyboardTypeNumberPad; |
| 7307 | break; |
| 7308 | case WebKit::InputType::None: |
| 7309 | case WebKit::InputType::ContentEditable: |
| 7310 | case WebKit::InputType::Text: |
| 7311 | case WebKit::InputType::Password: |
| 7312 | case WebKit::InputType::TextArea: |
| 7313 | case WebKit::InputType::Search: |
| 7314 | case WebKit::InputType::Date: |
| 7315 | case WebKit::InputType::DateTimeLocal: |
| 7316 | case WebKit::InputType::Month: |
| 7317 | case WebKit::InputType::Week: |
| 7318 | case WebKit::InputType::Time: |
| 7319 | case WebKit::InputType::Select: |
| 7320 | case WebKit::InputType::Drawing: |
| 7321 | case WebKit::InputType::Color: |
| 7322 | traits.keyboardType = UIKeyboardTypeDefault; |
| 7323 | } |
| 7324 | break; |
| 7325 | case WebCore::InputMode::Text: |
| 7326 | traits.keyboardType = UIKeyboardTypeDefault; |
| 7327 | break; |
| 7328 | case WebCore::InputMode::Telephone: |
| 7329 | traits.keyboardType = UIKeyboardTypePhonePad; |
| 7330 | break; |
| 7331 | case WebCore::InputMode::Url: |
| 7332 | traits.keyboardType = UIKeyboardTypeURL; |
| 7333 | break; |
| 7334 | case WebCore::InputMode::Email: |
| 7335 | traits.keyboardType = UIKeyboardTypeEmailAddress; |
| 7336 | break; |
| 7337 | case WebCore::InputMode::Numeric: |
| 7338 | traits.keyboardType = UIKeyboardTypeNumberPad; |
| 7339 | break; |
| 7340 | case WebCore::InputMode::Decimal: |
| 7341 | traits.keyboardType = UIKeyboardTypeDecimalPad; |
| 7342 | break; |
| 7343 | case WebCore::InputMode::Search: |
| 7344 | traits.keyboardType = UIKeyboardTypeWebSearch; |
| 7345 | break; |
| 7346 | } |
| 7347 | |
| 7348 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 7349 | traits.textContentType = self.textContentTypeForQuickboard; |
| 7350 | #else |
| 7351 | traits.textContentType = [self contentTypeFromFieldName:_focusedElementInformation.autofillFieldName]; |
| 7352 | #endif |
| 7353 | |
| 7354 | auto extendedTraits = dynamic_objc_cast<WKExtendedTextInputTraits>(traits); |
| 7355 | auto privateTraits = (id <UITextInputTraits_Private>)traits; |
| 7356 | |
| 7357 | BOOL isSingleLineDocument = ^{ |
| 7358 | switch (_focusedElementInformation.elementType) { |
| 7359 | case WebKit::InputType::ContentEditable: |
| 7360 | case WebKit::InputType::TextArea: |
| 7361 | case WebKit::InputType::None: |
| 7362 | return NO__objc_no; |
| 7363 | case WebKit::InputType::Color: |
| 7364 | case WebKit::InputType::Date: |
| 7365 | case WebKit::InputType::DateTimeLocal: |
| 7366 | case WebKit::InputType::Drawing: |
| 7367 | case WebKit::InputType::Email: |
| 7368 | case WebKit::InputType::Month: |
| 7369 | case WebKit::InputType::Number: |
| 7370 | case WebKit::InputType::NumberPad: |
| 7371 | case WebKit::InputType::Password: |
| 7372 | case WebKit::InputType::Phone: |
| 7373 | case WebKit::InputType::Search: |
| 7374 | case WebKit::InputType::Select: |
| 7375 | case WebKit::InputType::Text: |
| 7376 | case WebKit::InputType::Time: |
| 7377 | case WebKit::InputType::URL: |
| 7378 | case WebKit::InputType::Week: |
| 7379 | return YES__objc_yes; |
| 7380 | } |
| 7381 | }(); |
| 7382 | |
| 7383 | if ([extendedTraits respondsToSelector:@selector(setSingleLineDocument:)]) |
| 7384 | extendedTraits.singleLineDocument = isSingleLineDocument; |
| 7385 | else if ([privateTraits respondsToSelector:@selector(setIsSingleLineDocument:)]) |
| 7386 | privateTraits.isSingleLineDocument = isSingleLineDocument; |
| 7387 | |
| 7388 | if (_focusedElementInformation.hasEverBeenPasswordField) { |
| 7389 | if ([privateTraits respondsToSelector:@selector(setLearnsCorrections:)]) |
| 7390 | privateTraits.learnsCorrections = NO__objc_no; |
| 7391 | extendedTraits.typingAdaptationEnabled = NO__objc_no; |
| 7392 | } |
| 7393 | |
| 7394 | if ([privateTraits respondsToSelector:@selector(setShortcutConversionType:)]) |
| 7395 | privateTraits.shortcutConversionType = _focusedElementInformation.elementType == WebKit::InputType::Password ? UITextShortcutConversionTypeNo : UITextShortcutConversionTypeDefault; |
| 7396 | |
| 7397 | #if HAVE(INLINE_PREDICTIONS)(defined 1 && 1) |
| 7398 | bool allowsInlinePredictions = [&] { |
| 7399 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 7400 | return false; |
| 7401 | #else |
| 7402 | if (self.webView.configuration.allowsInlinePredictions) |
| 7403 | return true; |
| 7404 | if (auto& state = protect(_page)->editorState(); state.hasPostLayoutData() && !_isFocusingElementWithKeyboard && !protect(_page)->waitingForPostLayoutEditorStateUpdateAfterFocusingElement()) |
| 7405 | return state.postLayoutData->canEnableWritingSuggestions; |
| 7406 | return _focusedElementInformation.isWritingSuggestionsEnabled; |
| 7407 | #endif |
| 7408 | }(); |
| 7409 | traits.inlinePredictionType = allowsInlinePredictions ? UITextInlinePredictionTypeDefault : UITextInlinePredictionTypeNo; |
| 7410 | #endif |
| 7411 | |
| 7412 | [self _updateTextInputTraitsForInteractionTintColor]; |
| 7413 | } |
| 7414 | |
| 7415 | - (UITextInteractionAssistant *)interactionAssistant |
| 7416 | { |
| 7417 | if (!self.shouldUseAsyncInteractions) |
| 7418 | return [_textInteractionWrapper textInteractionAssistant]; |
| 7419 | return super.interactionAssistant; |
| 7420 | } |
| 7421 | |
| 7422 | // NSRange support. Would like to deprecate to the extent possible, although some support |
| 7423 | // (i.e. selectionRange) has shipped as API. |
| 7424 | - (NSRange)selectionRange |
| 7425 | { |
| 7426 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG190" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7426, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7427 | |
| 7428 | return NSMakeRange(NSNotFound, 0); |
| 7429 | } |
| 7430 | |
| 7431 | - (CGRect)rectForNSRange:(NSRange)range |
| 7432 | { |
| 7433 | return CGRectZero; |
| 7434 | } |
| 7435 | |
| 7436 | - (NSRange)_markedTextNSRange |
| 7437 | { |
| 7438 | return NSMakeRange(NSNotFound, 0); |
| 7439 | } |
| 7440 | |
| 7441 | // DOM range support. |
| 7442 | - (DOMRange *)selectedDOMRange |
| 7443 | { |
| 7444 | return nilnullptr; |
| 7445 | } |
| 7446 | |
| 7447 | - (void)setSelectedDOMRange:(DOMRange *)range affinityDownstream:(BOOL)affinityDownstream |
| 7448 | { |
| 7449 | } |
| 7450 | |
| 7451 | // Modify text without starting a new undo grouping. |
| 7452 | - (void)replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text |
| 7453 | { |
| 7454 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG191" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7454, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7455 | } |
| 7456 | |
| 7457 | // Caret rect support. Shouldn't be necessary, but firstRectForRange doesn't offer precisely |
| 7458 | // the same functionality. |
| 7459 | - (CGRect)rectContainingCaretSelection |
| 7460 | { |
| 7461 | return CGRectZero; |
| 7462 | } |
| 7463 | |
| 7464 | - (BOOL)_isTextInputContextFocused:(_WKTextInputContext *)context |
| 7465 | { |
| 7466 | if (!context) { |
| 7467 | ASSERT_NOT_REACHED()((void)0); |
| 7468 | return NO__objc_no; |
| 7469 | } |
| 7470 | // We ignore bounding rect changes as the bounding rect of the focused element is not kept up-to-date. |
| 7471 | return [self _isSameAsFocusedElement:context._textInputContext]; |
| 7472 | } |
| 7473 | |
| 7474 | - (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler |
| 7475 | { |
| 7476 | ASSERT(context)((void)0); |
| 7477 | // This function can be called more than once during a text interaction (e.g. <rdar://problem/59430806>). |
| 7478 | if (![self becomeFirstResponder]) { |
| 7479 | completionHandler(nilnullptr); |
| 7480 | return; |
| 7481 | } |
| 7482 | if ([self _isTextInputContextFocused:context]) { |
| 7483 | completionHandler(_focusedElementInformation.isReadOnly ? nilnullptr : self); |
| 7484 | return; |
| 7485 | } |
| 7486 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 7487 | _usingGestureForSelection = YES__objc_yes; |
| 7488 | auto checkFocusedElement = [weakSelf = WeakObjCPtr<WKContentView> { self }, context = adoptNS([context copy]), completionHandler = makeBlockPtr(completionHandler)] (bool success) { |
| 7489 | auto strongSelf = weakSelf.get(); |
| 7490 | if (!strongSelf) { |
| 7491 | completionHandler(nilnullptr); |
| 7492 | return; |
| 7493 | } |
| 7494 | bool isFocused = success && [strongSelf _isTextInputContextFocused:context.get()]; |
| 7495 | bool isEditable = success && !strongSelf->_focusedElementInformation.isReadOnly; |
| 7496 | strongSelf->_textInteractionDidChangeFocusedElement |= isFocused; |
| 7497 | strongSelf->_usingGestureForSelection = NO__objc_no; |
| 7498 | completionHandler(isFocused && isEditable ? strongSelf.get() : nilnullptr); |
| 7499 | }; |
| 7500 | protect(_page)->focusTextInputContextAndPlaceCaret(context._textInputContext, WebCore::IntPoint { point }, WTF::move(checkFocusedElement)); |
| 7501 | } |
| 7502 | |
| 7503 | - (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler |
| 7504 | { |
| 7505 | WebCore::FloatRect searchRect { rect }; |
| 7506 | #if ENABLE(EDITABLE_REGION)(defined 1 && 1) |
| 7507 | bool hitInteractionRect = self._hasFocusedElement && searchRect.inclusivelyIntersects(_focusedElementInformation.interactionRect); |
| 7508 | if (!self.webView._editable && !hitInteractionRect && !WebKit::mayContainEditableElementsInRect(self, rect)) { |
| 7509 | completionHandler(@[ ]); |
| 7510 | return; |
| 7511 | } |
| 7512 | #endif |
| 7513 | protect(_page)->textInputContextsInRect(searchRect, [weakSelf = WeakObjCPtr<WKContentView>(self), completionHandler = makeBlockPtr(completionHandler)] (const Vector<WebCore::ElementContext>& contexts) { |
| 7514 | auto strongSelf = weakSelf.get(); |
| 7515 | if (!strongSelf || contexts.isEmpty()) { |
| 7516 | completionHandler(@[ ]); |
| 7517 | return; |
| 7518 | } |
| 7519 | completionHandler(createNSArray(contexts, [] (const auto& context) { |
| 7520 | return adoptNS([[_WKTextInputContext alloc] _initWithTextInputContext:context]); |
| 7521 | }).get()); |
| 7522 | }); |
| 7523 | } |
| 7524 | |
| 7525 | - (void)_willBeginTextInteractionInTextInputContext:(_WKTextInputContext *)context |
| 7526 | { |
| 7527 | ASSERT(context)((void)0); |
| 7528 | |
| 7529 | protect(_page)->setCanShowPlaceholder(context._textInputContext, false); |
| 7530 | |
| 7531 | ++_activeTextInteractionCount; |
| 7532 | if (_activeTextInteractionCount > 1) |
| 7533 | return; |
| 7534 | |
| 7535 | _textInteractionDidChangeFocusedElement = NO__objc_no; |
| 7536 | protect(_page)->setShouldRevealCurrentSelectionAfterInsertion(false); |
| 7537 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 7538 | _usingGestureForSelection = YES__objc_yes; |
| 7539 | } |
| 7540 | |
| 7541 | - (void)_didFinishTextInteractionInTextInputContext:(_WKTextInputContext *)context |
| 7542 | { |
| 7543 | ASSERT(context)((void)0); |
| 7544 | |
| 7545 | protect(_page)->setCanShowPlaceholder(context._textInputContext, true); |
| 7546 | |
| 7547 | ASSERT(_activeTextInteractionCount > 0)((void)0); |
| 7548 | --_activeTextInteractionCount; |
| 7549 | if (_activeTextInteractionCount) |
| 7550 | return; |
| 7551 | |
| 7552 | _usingGestureForSelection = NO__objc_no; |
| 7553 | |
| 7554 | if (_textInteractionDidChangeFocusedElement) { |
| 7555 | // Mark to zoom to reveal the newly focused element on the next editor state update. |
| 7556 | // Then tell the web process to reveal the current selection, which will send us (the |
| 7557 | // UI process) an editor state update. |
| 7558 | _revealFocusedElementDeferrer = WebKit::RevealFocusedElementDeferrer::create(self, WebKit::RevealFocusedElementDeferralReason::EditorState); |
| 7559 | _page->setWaitingForPostLayoutEditorStateUpdateAfterFocusingElement(true); |
| 7560 | _textInteractionDidChangeFocusedElement = NO__objc_no; |
| 7561 | } |
| 7562 | |
| 7563 | protect(_page)->setShouldRevealCurrentSelectionAfterInsertion(true); |
| 7564 | } |
| 7565 | |
| 7566 | - (void)modifierFlagsDidChangeFrom:(UIKeyModifierFlags)oldFlags to:(UIKeyModifierFlags)newFlags |
| 7567 | { |
| 7568 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG192" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7568, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7569 | |
| 7570 | auto dispatchSyntheticFlagsChangedEvents = [&] (UIKeyModifierFlags flags, bool keyDown) { |
| 7571 | if (flags & UIKeyModifierShift) |
| 7572 | [self handleKeyWebEvent:adoptNS([[WKSyntheticFlagsChangedWebEvent alloc] initWithShiftState:keyDown]).get()]; |
| 7573 | if (flags & UIKeyModifierAlphaShift) |
| 7574 | [self handleKeyWebEvent:adoptNS([[WKSyntheticFlagsChangedWebEvent alloc] initWithCapsLockState:keyDown]).get()]; |
| 7575 | }; |
| 7576 | |
| 7577 | if (UIKeyModifierFlags removedFlags = oldFlags & ~newFlags) |
| 7578 | dispatchSyntheticFlagsChangedEvents(removedFlags, false); |
| 7579 | if (UIKeyModifierFlags addedFlags = newFlags & ~oldFlags) |
| 7580 | dispatchSyntheticFlagsChangedEvents(addedFlags, true); |
| 7581 | } |
| 7582 | |
| 7583 | - (BOOL)shouldSuppressUpdateCandidateView |
| 7584 | { |
| 7585 | return _candidateViewNeedsUpdate; |
| 7586 | } |
| 7587 | |
| 7588 | // Web events. |
| 7589 | - (BOOL)requiresKeyEvents |
| 7590 | { |
| 7591 | return YES__objc_yes; |
| 7592 | } |
| 7593 | |
| 7594 | - (BOOL)_tryToHandlePressesEvent:(UIPressesEvent *)event |
| 7595 | { |
| 7596 | bool isHardwareKeyboardEvent = !!event._hidEvent; |
| 7597 | // We only want to handle key event from the hardware keyboard when we are |
| 7598 | // first responder and we are not interacting with editable content. |
| 7599 | if (self.isFirstResponder && isHardwareKeyboardEvent && (_inputPeripheral || !protect(_page)->editorState().isContentEditable)) { |
| 7600 | if ([_inputPeripheral respondsToSelector:@selector(handleKeyEvent:)]) { |
| 7601 | if ([_inputPeripheral handleKeyEvent:event]) |
| 7602 | return YES__objc_yes; |
| 7603 | } |
| 7604 | if (!_seenHardwareKeyDownInNonEditableElement) { |
| 7605 | _seenHardwareKeyDownInNonEditableElement = YES__objc_yes; |
| 7606 | [self reloadInputViews]; |
| 7607 | } |
| 7608 | } |
| 7609 | return NO__objc_no; |
| 7610 | } |
| 7611 | |
| 7612 | - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event |
| 7613 | { |
| 7614 | if ([self _tryToHandlePressesEvent:event]) |
| 7615 | return; |
| 7616 | |
| 7617 | _isHandlingActivePressesEvent = YES__objc_yes; |
| 7618 | [super pressesBegan:presses withEvent:event]; |
| 7619 | } |
| 7620 | |
| 7621 | - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event |
| 7622 | { |
| 7623 | if ([self _tryToHandlePressesEvent:event]) |
| 7624 | return; |
| 7625 | |
| 7626 | _isHandlingActivePressesEvent = NO__objc_no; |
| 7627 | [super pressesEnded:presses withEvent:event]; |
| 7628 | } |
| 7629 | |
| 7630 | - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event |
| 7631 | { |
| 7632 | if ([self _tryToHandlePressesEvent:event]) |
| 7633 | return; |
| 7634 | |
| 7635 | [super pressesChanged:presses withEvent:event]; |
| 7636 | } |
| 7637 | |
| 7638 | - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event |
| 7639 | { |
| 7640 | if ([self _tryToHandlePressesEvent:event]) |
| 7641 | return; |
| 7642 | |
| 7643 | _isHandlingActivePressesEvent = NO__objc_no; |
| 7644 | [super pressesCancelled:presses withEvent:event]; |
| 7645 | } |
| 7646 | |
| 7647 | - (void)generateSyntheticEditingCommand:(WebKit::SyntheticEditingCommandType)command |
| 7648 | { |
| 7649 | protect(_page)->generateSyntheticEditingCommand(command); |
| 7650 | } |
| 7651 | |
| 7652 | - (void)handleKeyWebEvent:(::WebEvent *)theEvent |
| 7653 | { |
| 7654 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG193" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7654, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7655 | |
| 7656 | [self _internalHandleKeyWebEvent:theEvent]; |
| 7657 | } |
| 7658 | |
| 7659 | - (void)_internalHandleKeyWebEvent:(::WebEvent *)theEvent |
| 7660 | { |
| 7661 | protect(_page)->handleKeyboardEvent(WebKit::NativeWebKeyboardEvent(theEvent, WebKit::NativeWebKeyboardEvent::HandledByInputMethod::No)); |
| 7662 | } |
| 7663 | |
| 7664 | - (void)handleKeyWebEvent:(::WebEvent *)event withCompletionHandler:(void (^)(::WebEvent *theEvent, BOOL wasHandled))completionHandler |
| 7665 | { |
| 7666 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG194" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7666, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7667 | |
| 7668 | [self _internalHandleKeyWebEvent:event withCompletionHandler:completionHandler]; |
| 7669 | } |
| 7670 | |
| 7671 | - (BOOL)_deferKeyEventToInputMethodEditing:(::WebEvent *)event |
| 7672 | { |
| 7673 | if (!protect(_page)->editorState().isContentEditable && !_treatAsContentEditableUntilNextEditorStateUpdate) |
| 7674 | return NO__objc_no; |
| 7675 | |
| 7676 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 7677 | if (self.shouldUseAsyncInteractions) { |
| 7678 | RetainPtr inputDelegate = _asyncInputDelegate; |
| 7679 | [self _logMissingSystemInputDelegateIfNeeded:__PRETTY_FUNCTION__]; |
| 7680 | if (!inputDelegate) |
| 7681 | return NO__objc_no; |
| 7682 | |
| 7683 | auto context = adoptNS([[BEKeyEntryContext alloc] initWithKeyEntry:event.originalKeyEntry]); |
| 7684 | [context setDocumentEditable:YES__objc_yes]; |
| 7685 | [context setShouldEvaluateForInputSystemHandling:YES__objc_yes]; |
| 7686 | return [inputDelegate shouldDeferEventHandlingToSystemForTextInput:self.asBETextInput context:context.get()]; |
| 7687 | } |
| 7688 | #endif // USE(BROWSERENGINEKIT) |
| 7689 | |
| 7690 | return [[UIKeyboardImpl sharedInstance] handleKeyInputMethodCommandForCurrentEvent]; |
| 7691 | } |
| 7692 | |
| 7693 | - (void)_internalHandleKeyWebEvent:(::WebEvent *)event withCompletionHandler:(void (^)(::WebEvent *event, BOOL wasHandled))completionHandler |
| 7694 | { |
| 7695 | if (!isUIThread()) { |
| 7696 | RELEASE_LOG_FAULT(KeyHandling, "%s was invoked on a background thread.", __PRETTY_FUNCTION__)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogKeyHandling .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_FAULT; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("%s was invoked on a background thread."), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG195" ) = "%s was invoked on a background thread."; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("%s was invoked on a background thread.", __PRETTY_FUNCTION__ )]; _os_log_fault_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "%s was invoked on a background thread.", __PRETTY_FUNCTION__ ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 7697 | completionHandler(event, NO__objc_no); |
| 7698 | return; |
| 7699 | } |
| 7700 | |
| 7701 | #if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST ) |
| 7702 | bool hasPendingArrowKey = _keyWebEventHandlers.containsIf([](auto& eventAndCompletion) { |
| 7703 | switch ([eventAndCompletion.event keyCode]) { |
| 7704 | case VK_RIGHT0x27: |
| 7705 | case VK_LEFT0x25: |
| 7706 | case VK_DOWN0x28: |
| 7707 | case VK_UP0x26: |
| 7708 | return true; |
| 7709 | default: |
| 7710 | return false; |
| 7711 | } |
| 7712 | }); |
| 7713 | |
| 7714 | if (hasPendingArrowKey) { |
| 7715 | RELEASE_LOG_ERROR(KeyHandling, "Ignoring incoming key event due to pending arrow key event")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogKeyHandling .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Ignoring incoming key event due to pending arrow key event" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG196" ) = "Ignoring incoming key event due to pending arrow key event" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Ignoring incoming key event due to pending arrow key event" )]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Ignoring incoming key event due to pending arrow key event" ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 7716 | completionHandler(event, NO__objc_no); |
| 7717 | return; |
| 7718 | } |
| 7719 | #endif // PLATFORM(MACCATALYST) |
| 7720 | |
| 7721 | if (event.type == WebEventKeyDown) |
| 7722 | _isHandlingActiveKeyEvent = YES__objc_yes; |
| 7723 | |
| 7724 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture]; |
| 7725 | |
| 7726 | using HandledByInputMethod = WebKit::NativeWebKeyboardEvent::HandledByInputMethod; |
| 7727 | if ([self _deferKeyEventToInputMethodEditing:event]) { |
| 7728 | completionHandler(event, YES__objc_yes); |
| 7729 | _isDeferringKeyEventsToInputMethod = YES__objc_yes; |
| 7730 | protect(_page)->handleKeyboardEvent(WebKit::NativeWebKeyboardEvent(event, HandledByInputMethod::Yes)); |
| 7731 | return; |
| 7732 | } |
| 7733 | |
| 7734 | if (protect(_page)->handleKeyboardEvent(WebKit::NativeWebKeyboardEvent(event, HandledByInputMethod::No))) |
| 7735 | _keyWebEventHandlers.append({ event, makeBlockPtr(completionHandler) }); |
| 7736 | else |
| 7737 | completionHandler(event, NO__objc_no); |
| 7738 | } |
| 7739 | |
| 7740 | - (void)_didHandleKeyEvent:(::WebEvent *)event eventWasHandled:(BOOL)eventWasHandled |
| 7741 | { |
| 7742 | if ([event isKindOfClass:[WKSyntheticFlagsChangedWebEvent class]]) |
| 7743 | return; |
| 7744 | |
| 7745 | if (!(event.keyboardFlags & WebEventKeyboardInputModifierFlagsChanged)) |
| 7746 | [protect(_keyboardScrollingAnimator) handleKeyEvent:event]; |
| 7747 | |
| 7748 | if (event.type == WebEventKeyUp) |
| 7749 | _isHandlingActiveKeyEvent = NO__objc_no; |
| 7750 | |
| 7751 | auto indexOfHandlerToCall = _keyWebEventHandlers.findIf([event](auto& entry) { |
| 7752 | return entry.event == event; |
| 7753 | }); |
| 7754 | |
| 7755 | if (indexOfHandlerToCall == notFound) |
| 7756 | return; |
| 7757 | |
| 7758 | auto handler = _keyWebEventHandlers[indexOfHandlerToCall].completionBlock; |
| 7759 | if (!handler) { |
| 7760 | ASSERT_NOT_REACHED()((void)0); |
| 7761 | return; |
| 7762 | } |
| 7763 | |
| 7764 | _keyWebEventHandlers.removeAt(indexOfHandlerToCall); |
| 7765 | handler(event, eventWasHandled); |
| 7766 | } |
| 7767 | |
| 7768 | - (BOOL)_interpretKeyEvent:(::WebEvent *)event withContext:(WebKit::KeyEventInterpretationContext&&)context |
| 7769 | { |
| 7770 | SetForScope interpretKeyEventScope { _isInterpretingKeyEvent, YES__objc_yes }; |
| 7771 | |
| 7772 | BOOL isCharEvent = context.isCharEvent; |
| 7773 | |
| 7774 | RetainPtr scrollView = [self _scrollViewForScrollingNodeID:context.scrollingNode]; |
| 7775 | RetainPtr keyboardScrollingAnimator = _keyboardScrollingAnimator; |
| 7776 | if ([keyboardScrollingAnimator beginWithEvent:event scrollView:protect((WKBaseScrollView *)scrollView.get() ?: self.webView._scrollViewInternal)] || [keyboardScrollingAnimator scrollTriggeringKeyIsPressed]) |
| 7777 | return YES__objc_yes; |
| 7778 | |
| 7779 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 7780 | if (self.shouldUseAsyncInteractions) { |
| 7781 | RetainPtr systemDelegate = _asyncInputDelegate; |
| 7782 | [self _logMissingSystemInputDelegateIfNeeded:__PRETTY_FUNCTION__]; |
| 7783 | if (!systemDelegate) |
| 7784 | return NO__objc_no; |
| 7785 | |
| 7786 | auto context = adoptNS([[BEKeyEntryContext alloc] initWithKeyEntry:event.originalKeyEntry]); |
| 7787 | [context setDocumentEditable:protect(_page)->editorState().isContentEditable]; |
| 7788 | [context setShouldInsertCharacter:isCharEvent]; |
| 7789 | return [systemDelegate shouldDeferEventHandlingToSystemForTextInput:self.asBETextInput context:context.get()]; |
| 7790 | } |
| 7791 | #endif // USE(BROWSERENGINEKIT) |
| 7792 | |
| 7793 | if (event.keyboardFlags & WebEventKeyboardInputModifierFlagsChanged) |
| 7794 | return NO__objc_no; |
| 7795 | |
| 7796 | BOOL contentEditable = protect(_page)->editorState().isContentEditable; |
| 7797 | |
| 7798 | if (!contentEditable && event.isTabKey) |
| 7799 | return NO__objc_no; |
| 7800 | |
| 7801 | UIKeyboardImpl *keyboard = [UIKeyboardImpl sharedInstance]; |
| 7802 | |
| 7803 | if (!isCharEvent && [keyboard handleKeyTextCommandForCurrentEvent]) |
| 7804 | return YES__objc_yes; |
| 7805 | if (isCharEvent && [keyboard handleKeyAppCommandForCurrentEvent]) |
| 7806 | return YES__objc_yes; |
| 7807 | |
| 7808 | // Don't insert character for an unhandled Command-key key command. This matches iOS and Mac platform conventions. |
| 7809 | if (event.modifierFlags & WebEventFlagMaskCommandKey) |
| 7810 | return NO__objc_no; |
| 7811 | |
| 7812 | NSString *characters = event.characters; |
| 7813 | if (!characters.length) |
| 7814 | return NO__objc_no; |
| 7815 | |
| 7816 | switch ([characters characterAtIndex:0]) { |
| 7817 | case NSBackspaceCharacter: |
| 7818 | case NSDeleteCharacter: |
| 7819 | if (contentEditable) { |
| 7820 | [keyboard deleteFromInputWithFlags:event.keyboardFlags]; |
| 7821 | return YES__objc_yes; |
| 7822 | } |
| 7823 | break; |
| 7824 | case NSEnterCharacter: |
| 7825 | case NSCarriageReturnCharacter: |
| 7826 | if (contentEditable && isCharEvent) { |
| 7827 | // Map \r from HW keyboard to \n to match the behavior of the soft keyboard. |
| 7828 | [keyboard addInputString:@"\n" withFlags:0 withInputManagerHint:nilnullptr]; |
| 7829 | return YES__objc_yes; |
| 7830 | } |
| 7831 | break; |
| 7832 | default: |
| 7833 | if (contentEditable && isCharEvent) { |
| 7834 | [keyboard addInputString:event.characters withFlags:event.keyboardFlags withInputManagerHint:event.inputManagerHint]; |
| 7835 | return YES__objc_yes; |
| 7836 | } |
| 7837 | break; |
| 7838 | } |
| 7839 | |
| 7840 | return NO__objc_no; |
| 7841 | } |
| 7842 | |
| 7843 | - (NSArray<NSString *> *)filePickerAcceptedTypeIdentifiers |
| 7844 | { |
| 7845 | if (!_fileUploadPanel) |
| 7846 | return @[]; |
| 7847 | |
| 7848 | return [_fileUploadPanel acceptedTypeIdentifiers]; |
| 7849 | } |
| 7850 | |
| 7851 | - (void)dismissFilePicker |
| 7852 | { |
| 7853 | [_fileUploadPanel dismissIfNeededWithReason:WebKit::PickerDismissalReason::Testing]; |
| 7854 | } |
| 7855 | |
| 7856 | - (BOOL)isScrollableForKeyboardScrollViewAnimator:(WKKeyboardScrollViewAnimator *)animator |
| 7857 | { |
| 7858 | if (protect(_page)->editorState().isContentEditable) |
| 7859 | return NO__objc_no; |
| 7860 | |
| 7861 | if (_focusedElementInformation.elementType == WebKit::InputType::Select) |
| 7862 | return NO__objc_no; |
| 7863 | |
| 7864 | RetainPtr scrollView = animator.scrollView ?: self.webView.scrollView; |
| 7865 | if (!scrollView.get().scrollEnabled) |
| 7866 | return NO__objc_no; |
| 7867 | |
| 7868 | #if HAVE(UISCROLLVIEW_ALLOWS_KEYBOARD_SCROLLING)(defined 1 && 1) |
| 7869 | if (!scrollView.get().allowsKeyboardScrolling) |
| 7870 | return NO__objc_no; |
| 7871 | #endif |
| 7872 | |
| 7873 | return YES__objc_yes; |
| 7874 | } |
| 7875 | |
| 7876 | - (CGFloat)keyboardScrollViewAnimator:(WKKeyboardScrollViewAnimator *)animator distanceForIncrement:(WebCore::ScrollGranularity)increment inDirection:(WebCore::ScrollDirection)direction |
| 7877 | { |
| 7878 | BOOL directionIsHorizontal = direction == WebCore::ScrollDirection::ScrollLeft || direction == WebCore::ScrollDirection::ScrollRight; |
| 7879 | |
| 7880 | switch (increment) { |
| 7881 | case WebCore::ScrollGranularity::Document: { |
| 7882 | CGSize documentSize = [self convertRect:self.bounds toView:self.webView].size; |
| 7883 | return directionIsHorizontal ? documentSize.width : documentSize.height; |
| 7884 | } |
| 7885 | case WebCore::ScrollGranularity::Page: { |
| 7886 | CGSize pageSize = [self convertSize:CGSizeMake(0, WebCore::Scrollbar::pageStep(protect(_page)->unobscuredContentRect().height(), self.bounds.size.height)) toView:self.webView]; |
| 7887 | return directionIsHorizontal ? pageSize.width : pageSize.height; |
| 7888 | } |
| 7889 | case WebCore::ScrollGranularity::Line: |
| 7890 | return [self convertSize:CGSizeMake(0, WebCore::Scrollbar::pixelsPerLineStep()) toView:self.webView].height; |
| 7891 | case WebCore::ScrollGranularity::Pixel: |
| 7892 | return 0; |
| 7893 | } |
| 7894 | ASSERT_NOT_REACHED()((void)0); |
| 7895 | return 0; |
| 7896 | } |
| 7897 | |
| 7898 | - (void)keyboardScrollViewAnimatorWillScroll:(WKKeyboardScrollViewAnimator *)animator |
| 7899 | { |
| 7900 | _isKeyboardScrollingAnimationRunning = YES__objc_yes; |
| 7901 | [self willStartZoomOrScroll]; |
| 7902 | } |
| 7903 | |
| 7904 | - (void)keyboardScrollViewAnimatorDidFinishScrolling:(WKKeyboardScrollViewAnimator *)animator |
| 7905 | { |
| 7906 | [_webView.get() _didFinishScrolling:animator.scrollView]; |
| 7907 | _isKeyboardScrollingAnimationRunning = NO__objc_no; |
| 7908 | } |
| 7909 | |
| 7910 | - (BOOL)isKeyboardScrollingAnimationRunning |
| 7911 | { |
| 7912 | return _isKeyboardScrollingAnimationRunning; |
| 7913 | } |
| 7914 | |
| 7915 | - (void)_executeEditCommand:(NSString *)commandName |
| 7916 | { |
| 7917 | [self _executeEditCommand:commandName notifyDelegate:WebKit::NotifyInputDelegate::Yes]; |
| 7918 | } |
| 7919 | |
| 7920 | - (void)_executeEditCommand:(NSString *)commandName notifyDelegate:(WebKit::NotifyInputDelegate)notifyDelegate |
| 7921 | { |
| 7922 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 7923 | // FIXME: Editing commands are not considered by WebKit as user initiated even if they are the result |
| 7924 | // of keydown or keyup. We need to query the keyboard to determine if this was called from the keyboard |
| 7925 | // or not to know whether to tell WebKit to treat this command as user initiated or not. |
| 7926 | if (notifyDelegate == WebKit::NotifyInputDelegate::Yes) |
| 7927 | [self _internalBeginSelectionChange]; |
| 7928 | protect(_page)->executeEditCommand(commandName, { }, [view = retainPtr(self), notifyDelegate] { |
| 7929 | if (notifyDelegate == WebKit::NotifyInputDelegate::Yes) |
| 7930 | [view _internalEndSelectionChange]; |
| 7931 | }); |
| 7932 | } |
| 7933 | |
| 7934 | - (void)_deleteByWord |
| 7935 | { |
| 7936 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG197" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7936, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7937 | |
| 7938 | [self _executeEditCommand:@"deleteWordBackward"]; |
| 7939 | } |
| 7940 | |
| 7941 | - (void)_deleteForwardByWord |
| 7942 | { |
| 7943 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG198" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7943, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7944 | |
| 7945 | [self _executeEditCommand:@"deleteWordForward"]; |
| 7946 | } |
| 7947 | |
| 7948 | - (void)_deleteToStartOfLine |
| 7949 | { |
| 7950 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG199" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7950, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7951 | |
| 7952 | [self _executeEditCommand:@"deleteToBeginningOfLine"]; |
| 7953 | } |
| 7954 | |
| 7955 | - (void)_deleteToEndOfLine |
| 7956 | { |
| 7957 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG200" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7957, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7958 | |
| 7959 | [self _executeEditCommand:@"deleteToEndOfLine"]; |
| 7960 | } |
| 7961 | |
| 7962 | - (void)_deleteForwardAndNotify:(BOOL)notify |
| 7963 | { |
| 7964 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG201" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7964, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7965 | |
| 7966 | [self _executeEditCommand:@"deleteForward"]; |
| 7967 | } |
| 7968 | |
| 7969 | - (void)_deleteToEndOfParagraph |
| 7970 | { |
| 7971 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG202" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7971, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7972 | |
| 7973 | [self _executeEditCommand:@"deleteToEndOfParagraph"]; |
| 7974 | } |
| 7975 | |
| 7976 | - (void)_transpose |
| 7977 | { |
| 7978 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG203" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7978, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7979 | |
| 7980 | [self transposeCharactersAroundSelection]; |
| 7981 | } |
| 7982 | |
| 7983 | - (UITextInputArrowKeyHistory *)_moveUp:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 7984 | { |
| 7985 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG204" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7985, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7986 | |
| 7987 | [self _executeEditCommand:extending ? @"moveUpAndModifySelection" : @"moveUp"]; |
| 7988 | return nilnullptr; |
| 7989 | } |
| 7990 | |
| 7991 | - (UITextInputArrowKeyHistory *)_moveDown:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 7992 | { |
| 7993 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG205" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(7993, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 7994 | |
| 7995 | [self _executeEditCommand:extending ? @"moveDownAndModifySelection" : @"moveDown"]; |
| 7996 | return nilnullptr; |
| 7997 | } |
| 7998 | |
| 7999 | - (UITextInputArrowKeyHistory *)_moveLeft:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8000 | { |
| 8001 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG206" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8001, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8002 | |
| 8003 | [self _executeEditCommand:extending? @"moveLeftAndModifySelection" : @"moveLeft"]; |
| 8004 | return nilnullptr; |
| 8005 | } |
| 8006 | |
| 8007 | - (UITextInputArrowKeyHistory *)_moveRight:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8008 | { |
| 8009 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG207" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8009, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8010 | |
| 8011 | [self _executeEditCommand:extending ? @"moveRightAndModifySelection" : @"moveRight"]; |
| 8012 | return nilnullptr; |
| 8013 | } |
| 8014 | |
| 8015 | - (UITextInputArrowKeyHistory *)_moveToStartOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8016 | { |
| 8017 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG208" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8017, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8018 | |
| 8019 | [self _executeEditCommand:extending ? @"moveWordBackwardAndModifySelection" : @"moveWordBackward"]; |
| 8020 | return nilnullptr; |
| 8021 | } |
| 8022 | |
| 8023 | - (UITextInputArrowKeyHistory *)_moveToStartOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8024 | { |
| 8025 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG209" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8025, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8026 | |
| 8027 | [self _executeEditCommand:extending ? @"moveBackwardAndModifySelection" : @"moveBackward"]; |
| 8028 | [self _executeEditCommand:extending ? @"moveToBeginningOfParagraphAndModifySelection" : @"moveToBeginningOfParagraph"]; |
| 8029 | return nilnullptr; |
| 8030 | } |
| 8031 | |
| 8032 | - (UITextInputArrowKeyHistory *)_moveToStartOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8033 | { |
| 8034 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG210" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8034, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8035 | |
| 8036 | [self _executeEditCommand:extending ? @"moveToBeginningOfLineAndModifySelection" : @"moveToBeginningOfLine"]; |
| 8037 | return nilnullptr; |
| 8038 | } |
| 8039 | |
| 8040 | - (UITextInputArrowKeyHistory *)_moveToStartOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8041 | { |
| 8042 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG211" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8042, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8043 | |
| 8044 | [self _executeEditCommand:extending ? @"moveToBeginningOfDocumentAndModifySelection" : @"moveToBeginningOfDocument"]; |
| 8045 | return nilnullptr; |
| 8046 | } |
| 8047 | |
| 8048 | - (UITextInputArrowKeyHistory *)_moveToEndOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8049 | { |
| 8050 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG212" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8050, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8051 | |
| 8052 | [self _executeEditCommand:extending ? @"moveWordForwardAndModifySelection" : @"moveWordForward"]; |
| 8053 | return nilnullptr; |
| 8054 | } |
| 8055 | |
| 8056 | - (UITextInputArrowKeyHistory *)_moveToEndOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8057 | { |
| 8058 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG213" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8058, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8059 | |
| 8060 | [self _executeEditCommand:extending ? @"moveForwardAndModifySelection" : @"moveForward"]; |
| 8061 | [self _executeEditCommand:extending ? @"moveToEndOfParagraphAndModifySelection" : @"moveToEndOfParagraph"]; |
| 8062 | return nilnullptr; |
| 8063 | } |
| 8064 | |
| 8065 | - (UITextInputArrowKeyHistory *)_moveToEndOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8066 | { |
| 8067 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG214" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8067, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8068 | |
| 8069 | [self _executeEditCommand:extending ? @"moveToEndOfLineAndModifySelection" : @"moveToEndOfLine"]; |
| 8070 | return nilnullptr; |
| 8071 | } |
| 8072 | |
| 8073 | - (UITextInputArrowKeyHistory *)_moveToEndOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history |
| 8074 | { |
| 8075 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG215" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8075, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8076 | |
| 8077 | [self _executeEditCommand:extending ? @"moveToEndOfDocumentAndModifySelection" : @"moveToEndOfDocument"]; |
| 8078 | return nilnullptr; |
| 8079 | } |
| 8080 | |
| 8081 | // Sets a buffer to make room for autocorrection views |
| 8082 | - (void)setBottomBufferHeight:(CGFloat)bottomBuffer |
| 8083 | { |
| 8084 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG216" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8084, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8085 | } |
| 8086 | |
| 8087 | - (UIView *)automaticallySelectedOverlay |
| 8088 | { |
| 8089 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG217" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8089, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8090 | |
| 8091 | return [self unscaledView]; |
| 8092 | } |
| 8093 | |
| 8094 | - (UITextGranularity)selectionGranularity |
| 8095 | { |
| 8096 | return UITextGranularityCharacter; |
| 8097 | } |
| 8098 | |
| 8099 | // The can all be (and have been) trivially implemented in terms of UITextInput. Deprecate and remove. |
| 8100 | - (void)moveBackward:(unsigned)count |
| 8101 | { |
| 8102 | } |
| 8103 | |
| 8104 | - (void)moveForward:(unsigned)count |
| 8105 | { |
| 8106 | } |
| 8107 | |
| 8108 | - (unichar)characterBeforeCaretSelection |
| 8109 | { |
| 8110 | return 0; |
| 8111 | } |
| 8112 | |
| 8113 | - (NSString *)wordContainingCaretSelection |
| 8114 | { |
| 8115 | return nilnullptr; |
| 8116 | } |
| 8117 | |
| 8118 | - (DOMRange *)wordRangeContainingCaretSelection |
| 8119 | { |
| 8120 | return nilnullptr; |
| 8121 | } |
| 8122 | |
| 8123 | - (void)setMarkedText:(NSString *)text |
| 8124 | { |
| 8125 | } |
| 8126 | |
| 8127 | - (BOOL)hasContent |
| 8128 | { |
| 8129 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG218" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8129, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8130 | |
| 8131 | return self._hasContent; |
| 8132 | } |
| 8133 | |
| 8134 | - (BOOL)_hasContent |
| 8135 | { |
| 8136 | auto& editorState = protect(_page)->editorState(); |
| 8137 | return editorState.selectionType != WebCore::SelectionType::None && editorState.postLayoutData && editorState.postLayoutData->hasContent; |
| 8138 | } |
| 8139 | |
| 8140 | - (void)selectAll |
| 8141 | { |
| 8142 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG219" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8142, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8143 | } |
| 8144 | |
| 8145 | - (BOOL)hasSelection |
| 8146 | { |
| 8147 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG220" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8147, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8148 | |
| 8149 | return NO__objc_no; |
| 8150 | } |
| 8151 | |
| 8152 | - (BOOL)isPosition:(UITextPosition *)position atBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction |
| 8153 | { |
| 8154 | if (granularity == UITextGranularityParagraph) { |
| 8155 | Ref page = *_page; |
| 8156 | if (!page->editorState().postLayoutData) |
| 8157 | return NO__objc_no; |
| 8158 | |
| 8159 | if (direction == UITextStorageDirectionBackward && [position isEqual:self.selectedTextRange.start]) |
| 8160 | return page->editorState().postLayoutData->selectionStartIsAtParagraphBoundary; |
| 8161 | |
| 8162 | if (direction == UITextStorageDirectionForward && [position isEqual:self.selectedTextRange.end]) |
| 8163 | return page->editorState().postLayoutData->selectionEndIsAtParagraphBoundary; |
| 8164 | } |
| 8165 | |
| 8166 | return NO__objc_no; |
| 8167 | } |
| 8168 | |
| 8169 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position toBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction |
| 8170 | { |
| 8171 | return nilnullptr; |
| 8172 | } |
| 8173 | |
| 8174 | - (BOOL)isPosition:(UITextPosition *)position withinTextUnit:(UITextGranularity)granularity inDirection:(UITextDirection)direction |
| 8175 | { |
| 8176 | return NO__objc_no; |
| 8177 | } |
| 8178 | |
| 8179 | - (UITextRange *)rangeEnclosingPosition:(UITextPosition *)position withGranularity:(UITextGranularity)granularity inDirection:(UITextDirection)direction |
| 8180 | { |
| 8181 | return nilnullptr; |
| 8182 | } |
| 8183 | |
| 8184 | - (void)takeTraitsFrom:(UITextInputTraits *)traits |
| 8185 | { |
| 8186 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG221" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(8186, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 8187 | |
| 8188 | [[self textInputTraits] takeTraitsFrom:traits]; |
| 8189 | } |
| 8190 | |
| 8191 | - (void)_showKeyboard |
| 8192 | { |
| 8193 | [self setUpTextSelectionAssistant]; |
| 8194 | |
| 8195 | if (self.isFirstResponder && !_suppressSelectionAssistantReasons) |
| 8196 | [_textInteractionWrapper activateSelection]; |
| 8197 | |
| 8198 | [self reloadInputViews]; |
| 8199 | } |
| 8200 | |
| 8201 | - (void)_hideKeyboard:(BOOL)editableChanged |
| 8202 | { |
| 8203 | SetForScope isHidingKeyboardScope { _isHidingKeyboard, YES__objc_yes }; |
| 8204 | |
| 8205 | if (editableChanged) { |
| 8206 | _keyboardDismissedInCurrentPresentationUpdate = YES__objc_yes; |
| 8207 | protect(_page)->callAfterNextPresentationUpdate([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 8208 | if (RetainPtr strongSelf = weakSelf.get()) |
| 8209 | strongSelf->_keyboardDismissedInCurrentPresentationUpdate = NO__objc_no; |
| 8210 | }); |
| 8211 | } |
| 8212 | |
| 8213 | self.inputDelegate = nilnullptr; |
| 8214 | [self setUpTextSelectionAssistant]; |
| 8215 | |
| 8216 | [_textInteractionWrapper deactivateSelection]; |
| 8217 | [protect(_formAccessoryView) hideAutoFillButton]; |
| 8218 | |
| 8219 | // FIXME: Does it make sense to call -reloadInputViews on watchOS? |
| 8220 | [self reloadInputViews]; |
| 8221 | if (_formAccessoryView) |
| 8222 | [self _updateAccessory]; |
| 8223 | } |
| 8224 | |
| 8225 | - (BOOL)_formControlRefreshEnabled |
| 8226 | { |
| 8227 | if (!_page) |
| 8228 | return NO__objc_no; |
| 8229 | |
| 8230 | return YES__objc_yes; |
| 8231 | } |
| 8232 | |
| 8233 | - (const WebKit::FocusedElementInformation&)focusedElementInformation |
| 8234 | { |
| 8235 | return _focusedElementInformation; |
| 8236 | } |
| 8237 | |
| 8238 | - (Vector<WebKit::OptionItem>&)focusedSelectElementOptions |
| 8239 | { |
| 8240 | return _focusedElementInformation.selectOptions; |
| 8241 | } |
| 8242 | |
| 8243 | // Note that selectability is also affected by the CSS property user-select. |
| 8244 | static bool mayContainSelectableText(WebKit::InputType type) |
| 8245 | { |
| 8246 | switch (type) { |
| 8247 | case WebKit::InputType::None: |
| 8248 | // The following types have custom UI and do not look or behave like a text field. |
| 8249 | case WebKit::InputType::Color: |
| 8250 | case WebKit::InputType::Date: |
| 8251 | case WebKit::InputType::DateTimeLocal: |
| 8252 | case WebKit::InputType::Drawing: |
| 8253 | case WebKit::InputType::Month: |
| 8254 | case WebKit::InputType::Select: |
| 8255 | case WebKit::InputType::Time: |
| 8256 | #if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 8257 | case WebKit::InputType::Week: |
| 8258 | #endif |
| 8259 | return false; |
| 8260 | // The following types look and behave like a text field. |
| 8261 | case WebKit::InputType::ContentEditable: |
| 8262 | case WebKit::InputType::Email: |
| 8263 | case WebKit::InputType::Number: |
| 8264 | case WebKit::InputType::NumberPad: |
| 8265 | case WebKit::InputType::Password: |
| 8266 | case WebKit::InputType::Phone: |
| 8267 | case WebKit::InputType::Search: |
| 8268 | case WebKit::InputType::Text: |
| 8269 | case WebKit::InputType::TextArea: |
| 8270 | case WebKit::InputType::URL: |
| 8271 | #if !ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 8272 | case WebKit::InputType::Week: |
| 8273 | #endif |
| 8274 | return true; |
| 8275 | } |
| 8276 | } |
| 8277 | |
| 8278 | - (BOOL)_shouldShowKeyboardForElement:(const WebKit::FocusedElementInformation&)information |
| 8279 | { |
| 8280 | if (information.inputMode == WebCore::InputMode::None) |
| 8281 | return NO__objc_no; |
| 8282 | |
| 8283 | return [self _shouldShowKeyboardForElementIgnoringInputMode:information]; |
| 8284 | } |
| 8285 | |
| 8286 | - (BOOL)_shouldShowKeyboardForElementIgnoringInputMode:(const WebKit::FocusedElementInformation&)information |
| 8287 | { |
| 8288 | return mayContainSelectableText(information.elementType) || [self _elementTypeRequiresAccessoryView:information.elementType]; |
| 8289 | } |
| 8290 | |
| 8291 | static RetainPtr<NSObject <WKFormPeripheral>> createInputPeripheralWithView(WebKit::InputType type, WKContentView *view) |
| 8292 | { |
| 8293 | #if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 8294 | UNUSED_PARAM(type)(void)type; |
| 8295 | UNUSED_PARAM(view)(void)view; |
| 8296 | return nilnullptr; |
| 8297 | #else |
| 8298 | switch (type) { |
| 8299 | case WebKit::InputType::Select: |
| 8300 | // Don't create native iOS picker for appearance: base selects |
| 8301 | if (view.focusedElementInformation.usesBaseAppearancePicker) |
| 8302 | return nilnullptr; |
| 8303 | |
| 8304 | return adoptNS([[WKFormSelectControl alloc] initWithView:view]); |
| 8305 | case WebKit::InputType::Color: |
| 8306 | #if PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV ) |
| 8307 | return nilnullptr; |
| 8308 | #else |
| 8309 | return adoptNS([[WKFormColorControl alloc] initWithView:view]); |
| 8310 | #endif |
| 8311 | case WebKit::InputType::Date: |
| 8312 | case WebKit::InputType::DateTimeLocal: |
| 8313 | case WebKit::InputType::Month: |
| 8314 | case WebKit::InputType::Time: |
| 8315 | #if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 8316 | case WebKit::InputType::Week: |
| 8317 | #endif |
| 8318 | return adoptNS([[WKDateTimeInputControl alloc] initWithView:view]); |
| 8319 | default: |
| 8320 | return nilnullptr; |
| 8321 | } |
| 8322 | #endif |
| 8323 | } |
| 8324 | |
| 8325 | - (void)startDeferringInputViewUpdates:(WebKit::InputViewUpdateDeferralSources)sources |
| 8326 | { |
| 8327 | ASSERT(!sources.isEmpty())((void)0); |
| 8328 | if (_inputViewUpdateDeferralSources.isEmpty()) { |
| 8329 | RELEASE_LOG(TextInput, "Started deferring input view updates (%02x)", sources.toRaw())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInput .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Started deferring input view updates (%02x)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG222" ) = "Started deferring input view updates (%02x)"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Started deferring input view updates (%02x)", sources.toRaw ())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Started deferring input view updates (%02x)" , sources.toRaw()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 8330 | [self _beginPinningInputViews]; |
| 8331 | } |
| 8332 | |
| 8333 | _inputViewUpdateDeferralSources.add(sources); |
| 8334 | } |
| 8335 | |
| 8336 | - (void)stopDeferringInputViewUpdates:(WebKit::InputViewUpdateDeferralSources)sources |
| 8337 | { |
| 8338 | ASSERT(!sources.isEmpty())((void)0); |
| 8339 | if (!_inputViewUpdateDeferralSources.containsAny(sources)) |
| 8340 | return; |
| 8341 | |
| 8342 | _inputViewUpdateDeferralSources.remove(sources); |
| 8343 | |
| 8344 | if (_inputViewUpdateDeferralSources.isEmpty()) { |
| 8345 | [self _cancelPreviousResetInputViewDeferralRequest]; |
| 8346 | RELEASE_LOG(TextInput, "Stopped deferring input view updates (%02x)", sources.toRaw())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInput .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Stopped deferring input view updates (%02x)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG223" ) = "Stopped deferring input view updates (%02x)"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Stopped deferring input view updates (%02x)", sources.toRaw ())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Stopped deferring input view updates (%02x)" , sources.toRaw()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 8347 | [self _endPinningInputViews]; |
| 8348 | } |
| 8349 | } |
| 8350 | |
| 8351 | - (void)stopDeferringInputViewUpdatesForAllSources |
| 8352 | { |
| 8353 | if (_inputViewUpdateDeferralSources.isEmpty()) |
| 8354 | return; |
| 8355 | |
| 8356 | RELEASE_LOG(TextInput, "Stopped deferring all input view updates (%02x)", _inputViewUpdateDeferralSources.toRaw())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInput .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Stopped deferring all input view updates (%02x)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG224" ) = "Stopped deferring all input view updates (%02x)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Stopped deferring all input view updates (%02x)", _inputViewUpdateDeferralSources .toRaw())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Stopped deferring all input view updates (%02x)", _inputViewUpdateDeferralSources .toRaw()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 8357 | [self _cancelPreviousResetInputViewDeferralRequest]; |
| 8358 | _inputViewUpdateDeferralSources = { }; |
| 8359 | [self _endPinningInputViews]; |
| 8360 | } |
| 8361 | |
| 8362 | - (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode activityStateChanges:(OptionSet<WebCore::ActivityState>)activityStateChanges userObject:(NSObject <NSSecureCoding> *)userObject |
| 8363 | { |
| 8364 | CompletionHandlerCallingScope restoreValues([ |
| 8365 | weakSelf = WeakObjCPtr { self }, |
| 8366 | changingFocusValueToRestore = _isChangingFocus, |
| 8367 | focusingElementWithKeyboardValueToRestore = _isFocusingElementWithKeyboard |
| 8368 | ] { |
| 8369 | RetainPtr strongSelf = weakSelf.get(); |
| 8370 | if (!strongSelf) |
| 8371 | return; |
| 8372 | strongSelf->_isChangingFocus = changingFocusValueToRestore; |
| 8373 | strongSelf->_isFocusingElementWithKeyboard = focusingElementWithKeyboardValueToRestore; |
| 8374 | |
| 8375 | if (auto callback = std::exchange(strongSelf->_pendingRunModalJavaScriptDialogCallback, { })) |
| 8376 | callback(); |
| 8377 | |
| 8378 | constexpr OptionSet sourcesToStopDeferring { |
| 8379 | WebKit::InputViewUpdateDeferralSource::ChangingFocusedElement, |
| 8380 | WebKit::InputViewUpdateDeferralSource::BecomeFirstResponder |
| 8381 | }; |
| 8382 | [strongSelf stopDeferringInputViewUpdates:sourcesToStopDeferring]; |
| 8383 | }); |
| 8384 | |
| 8385 | _isChangingFocus = self._hasFocusedElement; |
| 8386 | _isFocusingElementWithKeyboard = [self _shouldShowKeyboardForElement:information]; |
| 8387 | |
| 8388 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 8389 | _didAccessoryTabInitiateFocus = _isChangingFocusUsingAccessoryTab; |
| 8390 | |
| 8391 | Ref page = *_page; |
| 8392 | RetainPtr inputDelegate = [_webView.get() _inputDelegate]; |
| 8393 | RetainPtr<WKFocusedElementInfo> focusedElementInfo = adoptNS([[WKFocusedElementInfo alloc] initWithFocusedElementInformation:information isUserInitiated:userIsInteracting webView:_webView.get().get() userObject:userObject]); |
| 8394 | |
| 8395 | _WKFocusStartsInputSessionPolicy startInputSessionPolicy = _WKFocusStartsInputSessionPolicyAuto; |
| 8396 | |
| 8397 | if ([inputDelegate respondsToSelector:@selector(_webView:focusShouldStartInputSession:)]) { |
| 8398 | if ([inputDelegate _webView:self.webView focusShouldStartInputSession:focusedElementInfo.get()]) |
| 8399 | startInputSessionPolicy = _WKFocusStartsInputSessionPolicyAllow; |
| 8400 | else |
| 8401 | startInputSessionPolicy = _WKFocusStartsInputSessionPolicyDisallow; |
| 8402 | } |
| 8403 | |
| 8404 | if ([inputDelegate respondsToSelector:@selector(_webView:decidePolicyForFocusedElement:)]) |
| 8405 | startInputSessionPolicy = [inputDelegate _webView:self.webView decidePolicyForFocusedElement:focusedElementInfo.get()]; |
| 8406 | |
| 8407 | BOOL shouldShowInputView = [&] { |
| 8408 | switch (startInputSessionPolicy) { |
| 8409 | case _WKFocusStartsInputSessionPolicyAuto: |
| 8410 | // The default behavior is to allow node assistance if the user is interacting. |
| 8411 | // We also allow node assistance if the keyboard already is showing, unless we're in extra zoom mode. |
| 8412 | if (userIsInteracting) |
| 8413 | return YES__objc_yes; |
| 8414 | |
| 8415 | if (_keyboardDismissedInCurrentPresentationUpdate && !_editingEndedByUser) |
| 8416 | return YES__objc_yes; |
| 8417 | |
| 8418 | if (self.isFirstResponder || _becomingFirstResponder) { |
| 8419 | // When the software keyboard is being used to enter an url, only the focus activity state is changing. |
| 8420 | // In this case, auto focus on the page being navigated to should be disabled, unless a hardware |
| 8421 | // keyboard is attached. |
| 8422 | if (activityStateChanges && activityStateChanges != WebCore::ActivityState::IsFocused) |
| 8423 | return YES__objc_yes; |
| 8424 | |
| 8425 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 8426 | if (_isChangingFocus && ![protect(_focusedFormControlView) isHidden]) |
| 8427 | return YES__objc_yes; |
| 8428 | #else |
| 8429 | if (_isChangingFocus) |
| 8430 | return YES__objc_yes; |
| 8431 | |
| 8432 | if ([self _shouldShowKeyboardForElementIgnoringInputMode:information] && UIKeyboard.isInHardwareKeyboardMode) |
| 8433 | return YES__objc_yes; |
| 8434 | #endif |
| 8435 | } |
| 8436 | return NO__objc_no; |
| 8437 | case _WKFocusStartsInputSessionPolicyAllow: |
| 8438 | return YES__objc_yes; |
| 8439 | case _WKFocusStartsInputSessionPolicyDisallow: |
| 8440 | return NO__objc_no; |
| 8441 | default: |
| 8442 | ASSERT_NOT_REACHED()((void)0); |
| 8443 | return NO__objc_no; |
| 8444 | } |
| 8445 | }(); |
| 8446 | |
| 8447 | // Do not present input peripherals if a validation message is being displayed. |
| 8448 | if (information.isFocusingWithValidationMessage && !_isFocusingElementWithKeyboard) |
| 8449 | shouldShowInputView = NO__objc_no; |
| 8450 | |
| 8451 | if (blurPreviousNode) { |
| 8452 | // Defer view updates until the end of this function to avoid a noticeable flash when switching focus |
| 8453 | // between elements that require the keyboard. |
| 8454 | [self startDeferringInputViewUpdates:WebKit::InputViewUpdateDeferralSource::ChangingFocusedElement]; |
| 8455 | [self _elementDidBlur]; |
| 8456 | } |
| 8457 | |
| 8458 | if (shouldShowInputView) |
| 8459 | _editingEndedByUser = NO__objc_no; |
| 8460 | |
| 8461 | if (!shouldShowInputView || information.elementType == WebKit::InputType::None) { |
| 8462 | page->setIsShowingInputViewForFocusedElement(false); |
| 8463 | return; |
| 8464 | } |
| 8465 | |
| 8466 | page->setIsShowingInputViewForFocusedElement(true); |
| 8467 | |
| 8468 | // FIXME: We should remove this check when we manage to send ElementDidFocus from the WebProcess |
| 8469 | // only when it is truly time to show the keyboard. |
| 8470 | if ([self _isSameAsFocusedElement:information.elementContext]) { |
| 8471 | if (_inputPeripheral) { |
| 8472 | if (!self.isFirstResponder) |
| 8473 | [self becomeFirstResponder]; |
| 8474 | [self accessoryOpen]; |
| 8475 | } |
| 8476 | return; |
| 8477 | } |
| 8478 | |
| 8479 | [_webView.get() _resetFocusPreservationCountAndReleaseActiveFocusState]; |
| 8480 | |
| 8481 | _focusRequiresStrongPasswordAssistance = NO__objc_no; |
| 8482 | _additionalContextForStrongPasswordAssistance = nilnullptr; |
| 8483 | |
| 8484 | _pendingFocusedElementIdentifier = information.identifier; |
| 8485 | |
| 8486 | if (mayContainSelectableText(information.elementType)) |
| 8487 | _page->setWaitingForPostLayoutEditorStateUpdateAfterFocusingElement(true); |
| 8488 | |
| 8489 | if ([inputDelegate respondsToSelector:@selector(_webView:focusRequiresStrongPasswordAssistance:)]) { |
| 8490 | [self _continueElementDidFocus:information |
| 8491 | requiresStrongPasswordAssistance:[inputDelegate _webView:self.webView focusRequiresStrongPasswordAssistance:focusedElementInfo.get()] |
| 8492 | focusedElementInfo:focusedElementInfo |
| 8493 | activityStateChanges:activityStateChanges |
| 8494 | restoreValues:WTF::move(restoreValues)]; |
| 8495 | } else if ([inputDelegate respondsToSelector:@selector(_webView:focusRequiresStrongPasswordAssistance:completionHandler:)]) { |
| 8496 | auto checker = WebKit::CompletionHandlerCallChecker::create(inputDelegate, @selector(_webView:focusRequiresStrongPasswordAssistance:completionHandler:)); |
| 8497 | [inputDelegate _webView:self.webView focusRequiresStrongPasswordAssistance:focusedElementInfo.get() completionHandler:makeBlockPtr([ |
| 8498 | weakSelf = RetainPtr { self }, |
| 8499 | checker = WTF::move(checker), |
| 8500 | information, |
| 8501 | focusedElementInfo, |
| 8502 | activityStateChanges, |
| 8503 | restoreValues = WTF::move(restoreValues) |
| 8504 | ] (BOOL result) mutable { |
| 8505 | if (checker->completionHandlerHasBeenCalled()) |
| 8506 | return; |
| 8507 | checker->didCallCompletionHandler(); |
| 8508 | RetainPtr strongSelf = weakSelf.get(); |
| 8509 | if (!strongSelf) |
| 8510 | return; |
| 8511 | [strongSelf _continueElementDidFocus:information |
| 8512 | requiresStrongPasswordAssistance:result |
| 8513 | focusedElementInfo:focusedElementInfo |
| 8514 | activityStateChanges:activityStateChanges |
| 8515 | restoreValues:WTF::move(restoreValues)]; |
| 8516 | }).get()]; |
| 8517 | } else { |
| 8518 | [self _continueElementDidFocus:information |
| 8519 | requiresStrongPasswordAssistance:NO__objc_no |
| 8520 | focusedElementInfo:focusedElementInfo |
| 8521 | activityStateChanges:activityStateChanges |
| 8522 | restoreValues:WTF::move(restoreValues)]; |
| 8523 | } |
| 8524 | } |
| 8525 | |
| 8526 | - (void)_continueElementDidFocus:(const WebKit::FocusedElementInformation&)information requiresStrongPasswordAssistance:(BOOL)requiresStrongPasswordAssistance focusedElementInfo:(RetainPtr<WKFocusedElementInfo>)focusedElementInfo activityStateChanges:(OptionSet<WebCore::ActivityState>)activityStateChanges restoreValues:(CompletionHandlerCallingScope&&)restoreValues |
| 8527 | { |
| 8528 | if (_pendingFocusedElementIdentifier != information.identifier) |
| 8529 | return; |
| 8530 | |
| 8531 | _focusRequiresStrongPasswordAssistance = requiresStrongPasswordAssistance; |
| 8532 | |
| 8533 | RetainPtr inputDelegate = [_webView.get() _inputDelegate]; |
| 8534 | |
| 8535 | if ([inputDelegate respondsToSelector:@selector(_webViewAdditionalContextForStrongPasswordAssistance:)]) |
| 8536 | _additionalContextForStrongPasswordAssistance = [inputDelegate _webViewAdditionalContextForStrongPasswordAssistance:self.webView]; |
| 8537 | else |
| 8538 | _additionalContextForStrongPasswordAssistance = @{ }; |
| 8539 | |
| 8540 | bool delegateImplementsWillStartInputSession = [inputDelegate respondsToSelector:@selector(_webView:willStartInputSession:)]; |
| 8541 | bool delegateImplementsDidStartInputSession = [inputDelegate respondsToSelector:@selector(_webView:didStartInputSession:)]; |
| 8542 | |
| 8543 | if (delegateImplementsWillStartInputSession || delegateImplementsDidStartInputSession) { |
| 8544 | [_formInputSession invalidate]; |
| 8545 | _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self focusedElementInfo:focusedElementInfo.get() requiresStrongPasswordAssistance:_focusRequiresStrongPasswordAssistance]); |
| 8546 | } |
| 8547 | |
| 8548 | if (delegateImplementsWillStartInputSession) |
| 8549 | [inputDelegate _webView:self.webView willStartInputSession:_formInputSession.get()]; |
| 8550 | |
| 8551 | BOOL requiresKeyboard = mayContainSelectableText(information.elementType); |
| 8552 | BOOL editableChanged = [self setIsEditable:requiresKeyboard]; |
| 8553 | _focusedElementInformation = information; |
| 8554 | _legacyTextInputTraits = nilnullptr; |
| 8555 | _extendedTextInputTraits = nilnullptr; |
| 8556 | |
| 8557 | Ref page = *_page; |
| 8558 | |
| 8559 | OptionSet<WebKit::RevealFocusedElementDeferralReason> revealFocusedElementDeferralReasons; |
| 8560 | // For elements that have selectable content (e.g. text field) we need to wait for the web process to send an up-to-date |
| 8561 | // selection rect before we can zoom and reveal the selection. Non-selectable elements (e.g. <select>) can be zoomed |
| 8562 | // immediately because they have no selection to reveal. |
| 8563 | // We create RevealFocusedElementDeferrer before callin becomeFirstResponder as the latter can result in _keyboardWillShow. |
| 8564 | if (requiresKeyboard) { |
| 8565 | bool ignorePreviousKeyboardWillShowNotification = [] { |
| 8566 | // In the case where out-of-process keyboard is enabled and the software keyboard is shown, |
| 8567 | // we end up getting two sets of "KeyboardWillShow" -> "KeyboardDidShow" notifications when |
| 8568 | // the keyboard animates up, after reloading input views. When the first set of notifications |
| 8569 | // is dispatched underneath the call to -reloadInputViews above, the keyboard hasn't yet |
| 8570 | // become full height, so attempts to reveal the focused element using the current height will |
| 8571 | // fail. The second set of keyboard notifications contains the final keyboard height, and is the |
| 8572 | // one we should use for revealing the focused element. |
| 8573 | // See also: <rdar://111704216>. |
| 8574 | if (!UIKeyboard.usesInputSystemUI) |
| 8575 | return false; |
| 8576 | |
| 8577 | auto keyboard = UIKeyboard.activeKeyboard; |
| 8578 | return keyboard && !keyboard.isMinimized; |
| 8579 | }(); |
| 8580 | |
| 8581 | if (page->waitingForPostLayoutEditorStateUpdateAfterFocusingElement()) |
| 8582 | revealFocusedElementDeferralReasons.add(WebKit::RevealFocusedElementDeferralReason::EditorState); |
| 8583 | |
| 8584 | if (!self._scroller.firstResponderKeyboardAvoidanceEnabled) |
| 8585 | revealFocusedElementDeferralReasons.add(WebKit::RevealFocusedElementDeferralReason::KeyboardDidShow); |
| 8586 | else if (_waitingForKeyboardAppearanceAnimationToStart || ignorePreviousKeyboardWillShowNotification) |
| 8587 | revealFocusedElementDeferralReasons.add(WebKit::RevealFocusedElementDeferralReason::KeyboardWillShow); |
| 8588 | |
| 8589 | if (!revealFocusedElementDeferralReasons.isEmpty()) |
| 8590 | _revealFocusedElementDeferrer = WebKit::RevealFocusedElementDeferrer::create(self, revealFocusedElementDeferralReasons); |
| 8591 | } |
| 8592 | |
| 8593 | if (![self isFirstResponder]) |
| 8594 | [self becomeFirstResponder]; |
| 8595 | |
| 8596 | #if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1) |
| 8597 | if (_focusedElementInformation.shouldHideSoftTopScrollEdgeEffect && ![[_webView.get() _wkScrollView] _usesHardTopScrollEdgeEffect]) |
| 8598 | [_webView.get() _addReasonToHideTopScrollPocket:WebKit::HideScrollPocketReason::SiteSpecificQuirk]; |
| 8599 | else |
| 8600 | [_webView.get() _removeReasonToHideTopScrollPocket:WebKit::HideScrollPocketReason::SiteSpecificQuirk]; |
| 8601 | #endif |
| 8602 | |
| 8603 | if (!_suppressSelectionAssistantReasons && requiresKeyboard && activityStateChanges.contains(WebCore::ActivityState::IsFocused)) { |
| 8604 | _treatAsContentEditableUntilNextEditorStateUpdate = YES__objc_yes; |
| 8605 | [_textInteractionWrapper activateSelection]; |
| 8606 | page->restoreSelectionInFocusedEditableElement(); |
| 8607 | page->scheduleFullEditorStateUpdate(); |
| 8608 | } |
| 8609 | |
| 8610 | _inputPeripheral = createInputPeripheralWithView(_focusedElementInformation.elementType, self); |
| 8611 | _waitingForKeyboardAppearanceAnimationToStart = requiresKeyboard && !_isChangingFocus; |
| 8612 | |
| 8613 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 8614 | [self addFocusedFormControlOverlay]; |
| 8615 | if (!_isChangingFocus) |
| 8616 | [self presentViewControllerForCurrentFocusedElement]; |
| 8617 | #else |
| 8618 | if (requiresKeyboard) { |
| 8619 | [self _showKeyboard]; |
| 8620 | if (!self.window.keyWindow) |
| 8621 | [self.window makeKeyWindow]; |
| 8622 | } else |
| 8623 | [self reloadInputViews]; |
| 8624 | #endif |
| 8625 | |
| 8626 | if (!UIKeyboard.activeKeyboard) { |
| 8627 | // The lack of keyboard here suggests that we're not running in a context where the keyboard can become visible |
| 8628 | // (e.g. when running API tests outside of the context of a UI application). In this scenario, don't bother waiting |
| 8629 | // for keyboard appearance notifications. |
| 8630 | _waitingForKeyboardAppearanceAnimationToStart = NO__objc_no; |
| 8631 | } |
| 8632 | |
| 8633 | // The custom fixed position rect behavior is affected by -isFocusingElement, so if that changes we need to recompute rects. |
| 8634 | if (editableChanged) |
| 8635 | [_webView.get() _scheduleVisibleContentRectUpdate]; |
| 8636 | |
| 8637 | if (!requiresKeyboard || revealFocusedElementDeferralReasons.isEmpty()) |
| 8638 | [self _zoomToRevealFocusedElement]; |
| 8639 | |
| 8640 | [self _updateAccessory]; |
| 8641 | |
| 8642 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 8643 | if (_isChangingFocus) |
| 8644 | [protect(_focusedFormControlView) reloadData:YES__objc_yes]; |
| 8645 | #endif |
| 8646 | |
| 8647 | // _inputPeripheral has been initialized in inputView called by reloadInputViews. |
| 8648 | [_inputPeripheral beginEditing]; |
| 8649 | |
| 8650 | if (delegateImplementsDidStartInputSession) |
| 8651 | [inputDelegate _webView:self.webView didStartInputSession:_formInputSession.get()]; |
| 8652 | |
| 8653 | [_webView.get() didStartFormControlInteraction]; |
| 8654 | } |
| 8655 | |
| 8656 | - (void)_elementDidBlur |
| 8657 | { |
| 8658 | SetForScope isBlurringFocusedElementForScope { _isBlurringFocusedElement, YES__objc_yes }; |
| 8659 | |
| 8660 | [_webView.get() _resetFocusPreservationCountAndReleaseActiveFocusState]; |
| 8661 | |
| 8662 | [self _endEditing]; |
| 8663 | |
| 8664 | #if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1) |
| 8665 | if (!_isChangingFocus) |
| 8666 | [_webView.get() _removeReasonToHideTopScrollPocket:WebKit::HideScrollPocketReason::SiteSpecificQuirk]; |
| 8667 | #endif |
| 8668 | |
| 8669 | [_formInputSession invalidate]; |
| 8670 | _formInputSession = nilnullptr; |
| 8671 | |
| 8672 | _dataListTextSuggestionsInputView = nilnullptr; |
| 8673 | _dataListTextSuggestions = nilnullptr; |
| 8674 | |
| 8675 | _pendingFocusedElementIdentifier = { }; |
| 8676 | |
| 8677 | BOOL editableChanged = [self setIsEditable:NO__objc_no]; |
| 8678 | // FIXME: We should completely invalidate _focusedElementInformation here, instead of a subset of individual members. |
| 8679 | _focusedElementInformation.elementType = WebKit::InputType::None; |
| 8680 | _focusedElementInformation.shouldSynthesizeKeyEventsForEditing = false; |
| 8681 | _focusedElementInformation.shouldAvoidResizingWhenInputViewBoundsChange = false; |
| 8682 | _focusedElementInformation.shouldAvoidScrollingWhenFocusedContentIsVisible = false; |
| 8683 | _focusedElementInformation.shouldHideSoftTopScrollEdgeEffect = false; |
| 8684 | _focusedElementInformation.shouldUseLegacySelectPopoverDismissalBehaviorInDataActivation = false; |
| 8685 | _focusedElementInformation.isFocusingWithValidationMessage = false; |
| 8686 | _focusedElementInformation.preventScroll = false; |
| 8687 | _inputPeripheral = nilnullptr; |
| 8688 | _focusRequiresStrongPasswordAssistance = NO__objc_no; |
| 8689 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 8690 | _additionalContextForStrongPasswordAssistance = nilnullptr; |
| 8691 | _waitingForKeyboardAppearanceAnimationToStart = NO__objc_no; |
| 8692 | _revealFocusedElementDeferrer = nullptr; |
| 8693 | |
| 8694 | // When defocusing an editable element reset a seen keydown before calling -_hideKeyboard so that we |
| 8695 | // re-evaluate whether we still need a keyboard when UIKit calls us back in -_requiresKeyboardWhenFirstResponder. |
| 8696 | if (editableChanged) |
| 8697 | _seenHardwareKeyDownInNonEditableElement = NO__objc_no; |
| 8698 | |
| 8699 | [self _hideKeyboard:editableChanged]; |
| 8700 | |
| 8701 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 8702 | [self dismissAllInputViewControllers:YES__objc_yes]; |
| 8703 | if (!_isChangingFocus) |
| 8704 | [self removeFocusedFormControlOverlay]; |
| 8705 | #endif |
| 8706 | |
| 8707 | if (editableChanged) { |
| 8708 | // The custom fixed position rect behavior is affected by -isFocusingElement, so if that changes we need to recompute rects. |
| 8709 | [_webView.get() _scheduleVisibleContentRectUpdate]; |
| 8710 | |
| 8711 | [_webView.get() didEndFormControlInteraction]; |
| 8712 | protect(_page)->setIsShowingInputViewForFocusedElement(false); |
| 8713 | } |
| 8714 | |
| 8715 | _page->setWaitingForPostLayoutEditorStateUpdateAfterFocusingElement(false); |
| 8716 | |
| 8717 | if (!_isChangingFocus) |
| 8718 | _didAccessoryTabInitiateFocus = NO__objc_no; |
| 8719 | |
| 8720 | _lastInsertedCharacterToOverrideCharacterBeforeSelection = std::nullopt; |
| 8721 | } |
| 8722 | |
| 8723 | - (void)_updateInputContextAfterBlurringAndRefocusingElement |
| 8724 | { |
| 8725 | if (!self._hasFocusedElement || !_suppressSelectionAssistantReasons) |
| 8726 | return; |
| 8727 | |
| 8728 | [self _internalInvalidateTextEntryContext]; |
| 8729 | } |
| 8730 | |
| 8731 | - (void)_didProgrammaticallyClearFocusedElement:(WebCore::ElementContext&&)context |
| 8732 | { |
| 8733 | if (![self _isSameAsFocusedElement:context]) |
| 8734 | return; |
| 8735 | |
| 8736 | [self _internalInvalidateTextEntryContext]; |
| 8737 | } |
| 8738 | |
| 8739 | - (void)_internalInvalidateTextEntryContext |
| 8740 | { |
| 8741 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 8742 | if (self.shouldUseAsyncInteractions) { |
| 8743 | [protect(_asyncInputDelegate) invalidateTextEntryContextForTextInput:self.asBETextInput]; |
| 8744 | return; |
| 8745 | } |
| 8746 | #endif |
| 8747 | |
| 8748 | [UIKeyboardImpl.activeInstance updateForChangedSelection]; |
| 8749 | } |
| 8750 | |
| 8751 | - (void)_updateFocusedElementInformation:(const WebKit::FocusedElementInformation&)information |
| 8752 | { |
| 8753 | if (![self _isSameAsFocusedElement:information.elementContext]) |
| 8754 | return; |
| 8755 | |
| 8756 | _focusedElementInformation = information; |
| 8757 | [_inputPeripheral updateEditing]; |
| 8758 | } |
| 8759 | |
| 8760 | - (BOOL)shouldIgnoreKeyboardWillHideNotification |
| 8761 | { |
| 8762 | // Ignore keyboard will hide notifications sent during rotation. They're just there for |
| 8763 | // backwards compatibility reasons and processing the will hide notification would |
| 8764 | // temporarily screw up the unobscured view area. |
| 8765 | if (UIPeripheralHost.sharedInstance.rotationState) |
| 8766 | return YES__objc_yes; |
| 8767 | |
| 8768 | if (_isChangingFocus && _isFocusingElementWithKeyboard) |
| 8769 | return YES__objc_yes; |
| 8770 | |
| 8771 | return NO__objc_no; |
| 8772 | } |
| 8773 | |
| 8774 | - (void)_hardwareKeyboardAvailabilityChanged |
| 8775 | { |
| 8776 | _seenHardwareKeyDownInNonEditableElement = NO__objc_no; |
| 8777 | [self reloadInputViews]; |
| 8778 | } |
| 8779 | |
| 8780 | - (void)_didUpdateInputMode:(WebCore::InputMode)mode |
| 8781 | { |
| 8782 | if (!self.inputDelegate || !self._hasFocusedElement) |
| 8783 | return; |
| 8784 | |
| 8785 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 8786 | _focusedElementInformation.inputMode = mode; |
| 8787 | [self reloadInputViews]; |
| 8788 | #endif |
| 8789 | } |
| 8790 | |
| 8791 | static BOOL allPasteboardItemOriginsMatchOrigin(UIPasteboard *pasteboard, const String& originIdentifier) |
| 8792 | { |
| 8793 | if (originIdentifier.isEmpty()) |
| 8794 | return NO__objc_no; |
| 8795 | |
| 8796 | RetainPtr indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])]; |
| 8797 | RetainPtr allCustomData = [pasteboard dataForPasteboardType:@(WebCore::PasteboardCustomData::cocoaType().characters()) inItemSet:indices.get()]; |
| 8798 | if (!allCustomData.get().count) |
| 8799 | return NO__objc_no; |
| 8800 | |
| 8801 | BOOL foundAtLeastOneMatchingIdentifier = NO__objc_no; |
| 8802 | for (NSData *data in allCustomData.get()) { |
| 8803 | if (!data.length) |
| 8804 | continue; |
| 8805 | |
| 8806 | auto buffer = WebCore::SharedBuffer::create(data); |
| 8807 | if (WebCore::PasteboardCustomData::fromSharedBuffer(buffer.get()).origin() != originIdentifier) |
| 8808 | return NO__objc_no; |
| 8809 | |
| 8810 | foundAtLeastOneMatchingIdentifier = YES__objc_yes; |
| 8811 | } |
| 8812 | |
| 8813 | return foundAtLeastOneMatchingIdentifier; |
| 8814 | } |
| 8815 | |
| 8816 | - (void)_requestDOMPasteAccessForCategory:(WebCore::DOMPasteAccessCategory)pasteAccessCategory requiresInteraction:(WebCore::DOMPasteRequiresInteraction)requiresInteraction elementRect:(const WebCore::IntRect&)elementRect originIdentifier:(const String&)originIdentifier completionHandler:(CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&)completionHandler |
| 8817 | { |
| 8818 | if (auto existingCompletionHandler = std::exchange(_domPasteRequestHandler, WTF::move(completionHandler))) { |
| 8819 | ASSERT_NOT_REACHED()((void)0); |
| 8820 | existingCompletionHandler(WebCore::DOMPasteAccessResponse::DeniedForGesture); |
| 8821 | } |
| 8822 | |
| 8823 | _domPasteRequestCategory = pasteAccessCategory; |
| 8824 | |
| 8825 | if (requiresInteraction == WebCore::DOMPasteRequiresInteraction::No && allPasteboardItemOriginsMatchOrigin(protect(pasteboardForAccessCategory(pasteAccessCategory)), originIdentifier)) { |
| 8826 | [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::GrantedForCommand]; |
| 8827 | return; |
| 8828 | } |
| 8829 | |
| 8830 | WebCore::IntRect menuControllerRect = elementRect; |
| 8831 | |
| 8832 | const CGFloat maximumElementWidth = 300; |
| 8833 | const CGFloat maximumElementHeight = 120; |
| 8834 | if (elementRect.isEmpty() || elementRect.width() > maximumElementWidth || elementRect.height() > maximumElementHeight) { |
| 8835 | const CGFloat interactionLocationMargin = 10; |
| 8836 | menuControllerRect = { WebCore::IntPoint(_lastInteractionLocation), { } }; |
| 8837 | menuControllerRect.inflate(interactionLocationMargin); |
| 8838 | } |
| 8839 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 8840 | [UIMenuController.sharedMenuController showMenuFromView:self rect:menuControllerRect]; |
| 8841 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 8842 | } |
| 8843 | |
| 8844 | - (void)doAfterEditorStateUpdateAfterFocusingElement:(dispatch_block_t)block |
| 8845 | { |
| 8846 | if (!_page->waitingForPostLayoutEditorStateUpdateAfterFocusingElement()) { |
| 8847 | block(); |
| 8848 | return; |
| 8849 | } |
| 8850 | |
| 8851 | _actionsToPerformAfterEditorStateUpdate.append(makeBlockPtr(block)); |
| 8852 | } |
| 8853 | |
| 8854 | - (void)_reconcileEnclosingScrollViewContentOffset:(WebKit::EditorState&)state |
| 8855 | { |
| 8856 | if (!state.hasVisualData()) |
| 8857 | return; |
| 8858 | |
| 8859 | auto scrollingNodeID = state.visualData->enclosingScrollingNodeID; |
| 8860 | RetainPtr scroller = [self _scrollViewForScrollingNodeID:scrollingNodeID]; |
| 8861 | if (!scroller) |
| 8862 | return; |
| 8863 | |
| 8864 | if ([_webView.get() _isInStableState:scroller.get()]) |
| 8865 | return; |
| 8866 | |
| 8867 | auto possiblyStaleScrollOffset = state.visualData->enclosingScrollOffset; |
| 8868 | auto scrollDelta = possiblyStaleScrollOffset - WebCore::roundedIntPoint([scroller contentOffset]); |
| 8869 | |
| 8870 | static constexpr auto adjustmentThreshold = 1; |
| 8871 | if (std::abs(scrollDelta.width()) <= adjustmentThreshold && std::abs(scrollDelta.height()) <= adjustmentThreshold) |
| 8872 | return; |
| 8873 | |
| 8874 | CGSize contentDelta = [scroller convertRect:CGRectMake(0, 0, scrollDelta.width(), scrollDelta.height()) toView:self].size; |
| 8875 | state.move(std::copysign(contentDelta.width, scrollDelta.width()), std::copysign(contentDelta.height, scrollDelta.height())); |
| 8876 | } |
| 8877 | |
| 8878 | - (void)_didUpdateEditorState |
| 8879 | { |
| 8880 | [self _updateInitialWritingDirectionIfNecessary]; |
| 8881 | |
| 8882 | // FIXME: If the initial writing direction just changed, we should wait until we get the next post-layout editor state |
| 8883 | // before zooming to reveal the selection rect. |
| 8884 | if (_revealFocusedElementDeferrer) |
| 8885 | protect(_revealFocusedElementDeferrer)->fulfill(WebKit::RevealFocusedElementDeferralReason::EditorState); |
| 8886 | |
| 8887 | _treatAsContentEditableUntilNextEditorStateUpdate = NO__objc_no; |
| 8888 | |
| 8889 | for (auto block : std::exchange(_actionsToPerformAfterEditorStateUpdate, { })) |
| 8890 | block(); |
| 8891 | } |
| 8892 | |
| 8893 | - (void)_updateInitialWritingDirectionIfNecessary |
| 8894 | { |
| 8895 | if (!_page->isEditable()) |
| 8896 | return; |
| 8897 | |
| 8898 | auto& editorState = protect(_page)->editorState(); |
| 8899 | if (editorState.selectionType != WebCore::SelectionType::Caret) |
| 8900 | return; |
| 8901 | |
| 8902 | UIKeyboardImpl *keyboard = UIKeyboardImpl.activeInstance; |
| 8903 | if (keyboard.delegate != self) |
| 8904 | return; |
| 8905 | |
| 8906 | // Synchronize the keyboard's writing direction with the newly received EditorState. |
| 8907 | [keyboard setInitialDirection]; |
| 8908 | } |
| 8909 | |
| 8910 | - (void)updateCurrentFocusedElementInformation:(Function<void(bool didUpdate)>&&)callback |
| 8911 | { |
| 8912 | WeakObjCPtr<WKContentView> weakSelf { self }; |
| 8913 | auto identifierBeforeUpdate = _focusedElementInformation.identifier; |
| 8914 | protect(_page)->requestFocusedElementInformation([callback = WTF::move(callback), identifierBeforeUpdate, weakSelf] (auto& info) { |
| 8915 | if (!weakSelf || !info || info->identifier != identifierBeforeUpdate) { |
| 8916 | // If the focused element may have changed in the meantime, don't overwrite focused element information. |
| 8917 | callback(false); |
| 8918 | return; |
| 8919 | } |
| 8920 | |
| 8921 | weakSelf.get()->_focusedElementInformation = info.value(); |
| 8922 | callback(true); |
| 8923 | }); |
| 8924 | } |
| 8925 | |
| 8926 | - (void)reloadContextViewForPresentedListViewController |
| 8927 | { |
| 8928 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 8929 | if ([_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]]) |
| 8930 | [(WKTextInputListViewController *)_presentedFullScreenInputViewController.get() reloadContextView]; |
| 8931 | #endif |
| 8932 | } |
| 8933 | |
| 8934 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 8935 | |
| 8936 | - (void)addFocusedFormControlOverlay |
| 8937 | { |
| 8938 | if (_focusedFormControlView) |
| 8939 | return; |
| 8940 | |
| 8941 | _activeFocusedStateRetainBlock = makeBlockPtr(self.webView._retainActiveFocusedState); |
| 8942 | |
| 8943 | _focusedFormControlView = adoptNS([[WKFocusedFormControlView alloc] initWithFrame:self.webView.bounds delegate:self]); |
| 8944 | [protect(_focusedFormControlView) hide:NO__objc_no]; |
| 8945 | [_webView.get() addSubview:_focusedFormControlView.get()]; |
| 8946 | [self setInputDelegate:static_cast<id<UITextInputDelegate>>(_focusedFormControlView.get())]; |
| 8947 | } |
| 8948 | |
| 8949 | - (void)removeFocusedFormControlOverlay |
| 8950 | { |
| 8951 | if (!_focusedFormControlView) |
| 8952 | return; |
| 8953 | |
| 8954 | if (auto releaseActiveFocusState = WTF::move(_activeFocusedStateRetainBlock)) |
| 8955 | releaseActiveFocusState(); |
| 8956 | |
| 8957 | [protect(_focusedFormControlView) removeFromSuperview]; |
| 8958 | _focusedFormControlView = nilnullptr; |
| 8959 | [self setInputDelegate:nilnullptr]; |
| 8960 | } |
| 8961 | |
| 8962 | - (RetainPtr<PUICTextInputContext>)createQuickboardTextInputContext |
| 8963 | { |
| 8964 | auto context = adoptNS([[PUICTextInputContext alloc] init]); |
| 8965 | [self _updateTextInputTraits:context.get()]; |
| 8966 | [context setInitialText:_focusedElementInformation.value.createNSString().get()]; |
| 8967 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 8968 | [context setAcceptsEmoji:YES__objc_yes]; |
| 8969 | [context setShouldPresentModernTextInputUI:YES__objc_yes]; |
| 8970 | [context setPlaceholder:self.inputLabelText]; |
| 8971 | #endif |
| 8972 | return context; |
| 8973 | } |
| 8974 | |
| 8975 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 8976 | |
| 8977 | - (RetainPtr<PUICQuickboardController>)_createQuickboardController:(UIViewController *)presentingViewController |
| 8978 | { |
| 8979 | auto quickboardController = adoptNS([[PUICQuickboardController alloc] init]); |
| 8980 | auto context = self.createQuickboardTextInputContext; |
| 8981 | [quickboardController setQuickboardPresentingViewController:presentingViewController]; |
| 8982 | [quickboardController setExcludedFromScreenCapture:[context isSecureTextEntry]]; |
| 8983 | [quickboardController setTextInputContext:context.get()]; |
| 8984 | [quickboardController setDelegate:self]; |
| 8985 | |
| 8986 | return quickboardController; |
| 8987 | } |
| 8988 | |
| 8989 | static bool canUseQuickboardControllerFor(UITextContentType type) |
| 8990 | { |
| 8991 | return [type isEqualToString:UITextContentTypePassword]; |
| 8992 | } |
| 8993 | |
| 8994 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 8995 | |
| 8996 | - (void)presentViewControllerForCurrentFocusedElement |
| 8997 | { |
| 8998 | [self dismissAllInputViewControllers:NO__objc_no]; |
| 8999 | |
| 9000 | _shouldRestoreFirstResponderStatusAfterLosingFocus = self.isFirstResponder; |
| 9001 | auto presentingViewController = self._wk_viewControllerForFullScreenPresentation; |
| 9002 | |
| 9003 | ASSERT(!_presentedFullScreenInputViewController)((void)0); |
| 9004 | |
| 9005 | BOOL prefersModalPresentation = NO__objc_no; |
| 9006 | |
| 9007 | switch (_focusedElementInformation.elementType) { |
| 9008 | case WebKit::InputType::Select: |
| 9009 | _presentedFullScreenInputViewController = adoptNS([[WKSelectMenuListViewController alloc] initWithDelegate:self]); |
| 9010 | break; |
| 9011 | case WebKit::InputType::Time: |
| 9012 | // Time inputs are special, in that the only UI affordances for dismissal are push buttons rather than status bar chevrons. |
| 9013 | // As such, modal presentation and dismissal is preferred even if a navigation stack exists. |
| 9014 | prefersModalPresentation = YES__objc_yes; |
| 9015 | _presentedFullScreenInputViewController = adoptNS([[WKTimePickerViewController alloc] initWithDelegate:self]); |
| 9016 | break; |
| 9017 | case WebKit::InputType::Date: |
| 9018 | _presentedFullScreenInputViewController = adoptNS([[WKDatePickerViewController alloc] initWithDelegate:self]); |
| 9019 | break; |
| 9020 | case WebKit::InputType::None: |
| 9021 | break; |
| 9022 | default: { |
| 9023 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 9024 | if (canUseQuickboardControllerFor(self.textContentTypeForQuickboard)) { |
| 9025 | _presentedQuickboardController = [self _createQuickboardController:presentingViewController]; |
| 9026 | break; |
| 9027 | } |
| 9028 | #endif |
| 9029 | _presentedFullScreenInputViewController = adoptNS([[WKTextInputListViewController alloc] initWithDelegate:self]); |
| 9030 | break; |
| 9031 | } |
| 9032 | } |
| 9033 | |
| 9034 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 9035 | ASSERT_IMPLIES(_presentedQuickboardController, !_presentedFullScreenInputViewController)((void)0); |
| 9036 | ASSERT_IMPLIES(_presentedFullScreenInputViewController, !_presentedQuickboardController)((void)0); |
| 9037 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 9038 | ASSERT(self._isPresentingFullScreenInputView)((void)0); |
| 9039 | ASSERT(presentingViewController)((void)0); |
| 9040 | |
| 9041 | if (!prefersModalPresentation && [presentingViewController isKindOfClass:[UINavigationController class]]) |
| 9042 | _inputNavigationViewControllerForFullScreenInputs = (UINavigationController *)presentingViewController; |
| 9043 | else |
| 9044 | _inputNavigationViewControllerForFullScreenInputs = nilnullptr; |
| 9045 | |
| 9046 | RetainPtr<UIViewController> controller; |
| 9047 | if (_presentedFullScreenInputViewController) { |
| 9048 | // 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. |
| 9049 | // This is because the HI specification (for certain scenarios) calls for navigation-style view controller presentation, but WKWebView can't make any guarantees |
| 9050 | // 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 |
| 9051 | // UINavigationController to present the web view. |
| 9052 | if (_inputNavigationViewControllerForFullScreenInputs) |
| 9053 | [_inputNavigationViewControllerForFullScreenInputs pushViewController:_presentedFullScreenInputViewController.get() animated:YES__objc_yes]; |
| 9054 | else |
| 9055 | [presentingViewController presentViewController:_presentedFullScreenInputViewController.get() animated:YES__objc_yes completion:nilnullptr]; |
| 9056 | controller = _presentedFullScreenInputViewController.get(); |
| 9057 | } |
| 9058 | |
| 9059 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 9060 | if (_presentedQuickboardController) { |
| 9061 | [_presentedQuickboardController present]; |
| 9062 | controller = [presentingViewController presentedViewController]; |
| 9063 | } |
| 9064 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 9065 | |
| 9066 | // Presenting a fullscreen input view controller fully obscures the web view. Without taking this token, the web content process will get backgrounded. |
| 9067 | _page->legacyMainFrameProcess().startBackgroundActivityForFullscreenInput(); |
| 9068 | |
| 9069 | // FIXME: PUICQuickboardController does not present its view controller immediately, since it asynchronously |
| 9070 | // establishes a connection to QuickboardViewService before presenting the remote view controller. |
| 9071 | // Fixing this requires a version of `-[PUICQuickboardController present]` that takes a completion handler. |
| 9072 | [presentingViewController.transitionCoordinator animateAlongsideTransition:nilnullptr completion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), controller] (id <UIViewControllerTransitionCoordinatorContext>) { |
| 9073 | auto strongWebView = weakWebView.get(); |
| 9074 | id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]); |
| 9075 | if ([uiDelegate respondsToSelector:@selector(_webView:didPresentFocusedElementViewController:)]) |
| 9076 | [uiDelegate _webView:strongWebView.get() didPresentFocusedElementViewController:controller.get()]; |
| 9077 | }]; |
| 9078 | } |
| 9079 | |
| 9080 | - (BOOL)_isPresentingFullScreenInputView |
| 9081 | { |
| 9082 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 9083 | if (_presentedQuickboardController) |
| 9084 | return YES__objc_yes; |
| 9085 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 9086 | return !!_presentedFullScreenInputViewController; |
| 9087 | } |
| 9088 | |
| 9089 | - (void)dismissAllInputViewControllers:(BOOL)animated |
| 9090 | { |
| 9091 | auto navigationController = std::exchange(_inputNavigationViewControllerForFullScreenInputs, nilnullptr); |
| 9092 | |
| 9093 | if (!self._isPresentingFullScreenInputView) { |
| 9094 | ASSERT(!navigationController)((void)0); |
| 9095 | return; |
| 9096 | } |
| 9097 | |
| 9098 | if (auto controller = std::exchange(_presentedFullScreenInputViewController, nilnullptr)) { |
| 9099 | auto dispatchDidDismiss = makeBlockPtr([weakWebView = WeakObjCPtr<WKWebView>(_webView), controller] { |
| 9100 | auto strongWebView = weakWebView.get(); |
| 9101 | id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]); |
| 9102 | if ([uiDelegate respondsToSelector:@selector(_webView:didDismissFocusedElementViewController:)]) |
| 9103 | [uiDelegate _webView:strongWebView.get() didDismissFocusedElementViewController:controller.get()]; |
| 9104 | }); |
| 9105 | |
| 9106 | if ([navigationController viewControllers].lastObject == controller.get()) { |
| 9107 | [navigationController popViewControllerAnimated:animated]; |
| 9108 | [[controller transitionCoordinator] animateAlongsideTransition:nilnullptr completion:[dispatchDidDismiss = WTF::move(dispatchDidDismiss)] (id <UIViewControllerTransitionCoordinatorContext>) { |
| 9109 | dispatchDidDismiss(); |
| 9110 | }]; |
| 9111 | } else if (auto presentedViewController = retainPtr([controller presentedViewController])) { |
| 9112 | [presentedViewController dismissViewControllerAnimated:animated completion:[controller, animated, dispatchDidDismiss = WTF::move(dispatchDidDismiss)] { |
| 9113 | [controller dismissViewControllerAnimated:animated completion:dispatchDidDismiss.get()]; |
| 9114 | }]; |
| 9115 | } else |
| 9116 | [controller dismissViewControllerAnimated:animated completion:dispatchDidDismiss.get()]; |
| 9117 | } |
| 9118 | |
| 9119 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 9120 | if (auto controller = std::exchange(_presentedQuickboardController, nilnullptr)) { |
| 9121 | auto presentedViewController = retainPtr([controller quickboardPresentingViewController].presentedViewController); |
| 9122 | [controller dismissWithCompletion:[weakWebView = WeakObjCPtr<WKWebView>(_webView), presentedViewController] { |
| 9123 | auto strongWebView = weakWebView.get(); |
| 9124 | id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([strongWebView UIDelegate]); |
| 9125 | if ([uiDelegate respondsToSelector:@selector(_webView:didDismissFocusedElementViewController:)]) |
| 9126 | [uiDelegate _webView:strongWebView.get() didDismissFocusedElementViewController:presentedViewController.get()]; |
| 9127 | }]; |
| 9128 | } |
| 9129 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 9130 | |
| 9131 | if (_shouldRestoreFirstResponderStatusAfterLosingFocus) { |
| 9132 | _shouldRestoreFirstResponderStatusAfterLosingFocus = NO__objc_no; |
| 9133 | if (!self.isFirstResponder) |
| 9134 | [self becomeFirstResponder]; |
| 9135 | } |
| 9136 | |
| 9137 | _page->legacyMainFrameProcess().endBackgroundActivityForFullscreenInput(); |
| 9138 | } |
| 9139 | |
| 9140 | - (void)focusedFormControlViewDidSubmit:(WKFocusedFormControlView *)view |
| 9141 | { |
| 9142 | [self insertText:@"\n"]; |
| 9143 | _page->blurFocusedElement(); |
| 9144 | } |
| 9145 | |
| 9146 | - (void)focusedFormControlViewDidCancel:(WKFocusedFormControlView *)view |
| 9147 | { |
| 9148 | _page->blurFocusedElement(); |
| 9149 | } |
| 9150 | |
| 9151 | - (void)focusedFormControlViewDidBeginEditing:(WKFocusedFormControlView *)view |
| 9152 | { |
| 9153 | [self updateCurrentFocusedElementInformation:[weakSelf = WeakObjCPtr<WKContentView>(self)] (bool didUpdate) { |
| 9154 | if (!didUpdate) |
| 9155 | return; |
| 9156 | |
| 9157 | auto strongSelf = weakSelf.get(); |
| 9158 | [strongSelf presentViewControllerForCurrentFocusedElement]; |
| 9159 | [strongSelf->_focusedFormControlView hide:YES__objc_yes]; |
| 9160 | }]; |
| 9161 | } |
| 9162 | |
| 9163 | - (CGRect)rectForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9164 | { |
| 9165 | return [self convertRect:_focusedElementInformation.interactionRect toView:view]; |
| 9166 | } |
| 9167 | |
| 9168 | - (CGRect)nextRectForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9169 | { |
| 9170 | if (!_focusedElementInformation.hasNextNode) |
| 9171 | return CGRectNull; |
| 9172 | |
| 9173 | return [self convertRect:_focusedElementInformation.nextNodeRect toView:view]; |
| 9174 | } |
| 9175 | |
| 9176 | - (CGRect)previousRectForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9177 | { |
| 9178 | if (!_focusedElementInformation.hasPreviousNode) |
| 9179 | return CGRectNull; |
| 9180 | |
| 9181 | return [self convertRect:_focusedElementInformation.previousNodeRect toView:view]; |
| 9182 | } |
| 9183 | |
| 9184 | - (UIScrollView *)scrollViewForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9185 | { |
| 9186 | return self._scroller; |
| 9187 | } |
| 9188 | |
| 9189 | - (NSString *)actionNameForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9190 | { |
| 9191 | if (_focusedElementInformation.formAction.isEmpty()) |
| 9192 | return nilnullptr; |
| 9193 | |
| 9194 | switch (_focusedElementInformation.elementType) { |
| 9195 | case WebKit::InputType::Select: |
| 9196 | case WebKit::InputType::Time: |
| 9197 | case WebKit::InputType::Date: |
| 9198 | case WebKit::InputType::Color: |
| 9199 | return nilnullptr; |
| 9200 | case WebKit::InputType::Search: |
| 9201 | return WebCore::formControlSearchButtonTitle().createNSString().autorelease(); |
| 9202 | default: |
| 9203 | return WebCore::formControlGoButtonTitle().createNSString().autorelease(); |
| 9204 | } |
| 9205 | } |
| 9206 | |
| 9207 | - (void)focusedFormControlViewDidRequestNextNode:(WKFocusedFormControlView *)view |
| 9208 | { |
| 9209 | if (_focusedElementInformation.hasNextNode) |
| 9210 | _page->focusNextFocusedElement(true, [] { }); |
| 9211 | } |
| 9212 | |
| 9213 | - (void)focusedFormControlViewDidRequestPreviousNode:(WKFocusedFormControlView *)view |
| 9214 | { |
| 9215 | if (_focusedElementInformation.hasPreviousNode) |
| 9216 | _page->focusNextFocusedElement(false, [] { }); |
| 9217 | } |
| 9218 | |
| 9219 | - (BOOL)hasNextNodeForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9220 | { |
| 9221 | return _focusedElementInformation.hasNextNode; |
| 9222 | } |
| 9223 | |
| 9224 | - (BOOL)hasPreviousNodeForFocusedFormControlView:(WKFocusedFormControlView *)view |
| 9225 | { |
| 9226 | return _focusedElementInformation.hasPreviousNode; |
| 9227 | } |
| 9228 | |
| 9229 | - (void)focusedFormControllerDidUpdateSuggestions:(WKFocusedFormControlView *)view |
| 9230 | { |
| 9231 | if (_isBlurringFocusedElement || ![_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]]) |
| 9232 | return; |
| 9233 | |
| 9234 | [(WKTextInputListViewController *)_presentedFullScreenInputViewController updateTextSuggestions:[protect(_focusedFormControlView) suggestions]]; |
| 9235 | } |
| 9236 | |
| 9237 | #pragma mark - WKSelectMenuListViewControllerDelegate |
| 9238 | |
| 9239 | - (void)selectMenu:(WKSelectMenuListViewController *)selectMenu didSelectItemAtIndex:(NSUInteger)index |
| 9240 | { |
| 9241 | ASSERT(!_focusedElementInformation.isMultiSelect)((void)0); |
| 9242 | [self updateFocusedElementSelectedIndex:index allowsMultipleSelection:false]; |
| 9243 | } |
| 9244 | |
| 9245 | - (NSUInteger)numberOfItemsInSelectMenu:(WKSelectMenuListViewController *)selectMenu |
| 9246 | { |
| 9247 | return self.focusedSelectElementOptions.size(); |
| 9248 | } |
| 9249 | |
| 9250 | - (NSString *)selectMenu:(WKSelectMenuListViewController *)selectMenu displayTextForItemAtIndex:(NSUInteger)index |
| 9251 | { |
| 9252 | auto& options = self.focusedSelectElementOptions; |
| 9253 | if (index >= options.size()) { |
| 9254 | ASSERT_NOT_REACHED()((void)0); |
| 9255 | return @""; |
| 9256 | } |
| 9257 | |
| 9258 | return options[index].text.createNSString().autorelease(); |
| 9259 | } |
| 9260 | |
| 9261 | - (void)selectMenu:(WKSelectMenuListViewController *)selectMenu didCheckItemAtIndex:(NSUInteger)index checked:(BOOL)checked |
| 9262 | { |
| 9263 | ASSERT(_focusedElementInformation.isMultiSelect)((void)0); |
| 9264 | if (index >= self.focusedSelectElementOptions.size()) { |
| 9265 | ASSERT_NOT_REACHED()((void)0); |
| 9266 | return; |
| 9267 | } |
| 9268 | |
| 9269 | auto& option = self.focusedSelectElementOptions[index]; |
| 9270 | if (option.isSelected == checked) { |
| 9271 | ASSERT_NOT_REACHED()((void)0); |
| 9272 | return; |
| 9273 | } |
| 9274 | |
| 9275 | [self updateFocusedElementSelectedIndex:index allowsMultipleSelection:true]; |
| 9276 | option.isSelected = checked; |
| 9277 | } |
| 9278 | |
| 9279 | - (BOOL)selectMenuUsesMultipleSelection:(WKSelectMenuListViewController *)selectMenu |
| 9280 | { |
| 9281 | return _focusedElementInformation.isMultiSelect; |
| 9282 | } |
| 9283 | |
| 9284 | - (BOOL)selectMenu:(WKSelectMenuListViewController *)selectMenu hasSelectedOptionAtIndex:(NSUInteger)index |
| 9285 | { |
| 9286 | if (index >= self.focusedSelectElementOptions.size()) { |
| 9287 | ASSERT_NOT_REACHED()((void)0); |
| 9288 | return NO__objc_no; |
| 9289 | } |
| 9290 | |
| 9291 | return self.focusedSelectElementOptions[index].isSelected; |
| 9292 | } |
| 9293 | |
| 9294 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 9295 | |
| 9296 | #pragma mark - PUICQuickboardControllerDelegate |
| 9297 | |
| 9298 | - (void)quickboardController:(PUICQuickboardController *)controller textInputValueDidChange:(NSAttributedString *)attributedText |
| 9299 | { |
| 9300 | _page->setTextAsync(attributedText.string); |
| 9301 | [self dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:controller]; |
| 9302 | } |
| 9303 | |
| 9304 | - (void)quickboardControllerTextInputValueCancelled:(PUICQuickboardController *)controller |
| 9305 | { |
| 9306 | [self dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:controller]; |
| 9307 | } |
| 9308 | |
| 9309 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 9310 | |
| 9311 | #endif // HAVE(PEPPER_UI_CORE) |
| 9312 | |
| 9313 | - (void)_wheelChangedWithEvent:(UIEvent *)event |
| 9314 | { |
| 9315 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 9316 | if ([protect(_focusedFormControlView) handleWheelEvent:event]) |
| 9317 | return; |
| 9318 | #endif |
| 9319 | [super _wheelChangedWithEvent:event]; |
| 9320 | } |
| 9321 | |
| 9322 | - (void)_updateSelectionAssistantSuppressionState |
| 9323 | { |
| 9324 | static const double minimumFocusedElementAreaForSuppressingSelectionAssistant = 4; |
| 9325 | |
| 9326 | auto& editorState = protect(_page)->editorState(); |
| 9327 | if (!editorState.hasPostLayoutAndVisualData()) |
| 9328 | return; |
| 9329 | |
| 9330 | BOOL selectionIsTransparentOrFullyClipped = NO__objc_no; |
| 9331 | BOOL focusedElementIsTooSmall = NO__objc_no; |
| 9332 | if (editorState.selectionType != WebCore::SelectionType::None) { |
| 9333 | auto& postLayoutData = *editorState.postLayoutData; |
| 9334 | auto& visualData = *editorState.visualData; |
| 9335 | if (postLayoutData.selectionIsTransparentOrFullyClipped) |
| 9336 | selectionIsTransparentOrFullyClipped = YES__objc_yes; |
| 9337 | |
| 9338 | if (self._hasFocusedElement) { |
| 9339 | auto elementArea = visualData.editableRootBounds.area<RecordOverflow>(); |
| 9340 | if (!elementArea.hasOverflowed() && elementArea < minimumFocusedElementAreaForSuppressingSelectionAssistant) |
| 9341 | focusedElementIsTooSmall = YES__objc_yes; |
| 9342 | } |
| 9343 | } |
| 9344 | |
| 9345 | if (selectionIsTransparentOrFullyClipped) |
| 9346 | [self _startSuppressingSelectionAssistantForReason:WebKit::SelectionIsTransparentOrFullyClipped]; |
| 9347 | else |
| 9348 | [self _stopSuppressingSelectionAssistantForReason:WebKit::SelectionIsTransparentOrFullyClipped]; |
| 9349 | |
| 9350 | if (focusedElementIsTooSmall) |
| 9351 | [self _startSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall]; |
| 9352 | else |
| 9353 | [self _stopSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall]; |
| 9354 | } |
| 9355 | |
| 9356 | - (void)_selectionChanged |
| 9357 | { |
| 9358 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 9359 | |
| 9360 | [self _updateSelectionAssistantSuppressionState]; |
| 9361 | |
| 9362 | _cachedSelectionContainerView = nilnullptr; |
| 9363 | _cachedSelectedTextRange = nilnullptr; |
| 9364 | _selectionNeedsUpdate = YES__objc_yes; |
| 9365 | // If we are changing the selection with a gesture there is no need |
| 9366 | // to wait to paint the selection. |
| 9367 | if (_usingGestureForSelection) |
| 9368 | [self _updateChangedSelection]; |
| 9369 | |
| 9370 | if (_candidateViewNeedsUpdate) { |
| 9371 | _candidateViewNeedsUpdate = NO__objc_no; |
| 9372 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 9373 | if (self.shouldUseAsyncInteractions) |
| 9374 | [protect(_asyncInputDelegate) invalidateTextEntryContextForTextInput:self.asBETextInput]; |
| 9375 | else |
| 9376 | #endif // USE(BROWSERENGINEKIT) |
| 9377 | { |
| 9378 | auto inputDelegate = self.inputDelegate; |
| 9379 | if ([inputDelegate respondsToSelector:@selector(layoutHasChanged)]) |
| 9380 | [(id<UITextInputDelegatePrivate>)inputDelegate layoutHasChanged]; |
| 9381 | } |
| 9382 | } |
| 9383 | |
| 9384 | [_webView.get() _didChangeEditorState]; |
| 9385 | |
| 9386 | Ref page = *_page; |
| 9387 | if (page->editorState().hasPostLayoutAndVisualData()) { |
| 9388 | _lastInsertedCharacterToOverrideCharacterBeforeSelection = std::nullopt; |
| 9389 | |
| 9390 | if (!_usingGestureForSelection && !_selectionChangeNestingLevel && page->editorState().triggeredByAccessibilitySelectionChange) { |
| 9391 | // Force UIKit to reload all EditorState-based UI; in particular, this includes text candidates. |
| 9392 | [self _internalBeginSelectionChange]; |
| 9393 | [self _internalEndSelectionChange]; |
| 9394 | } |
| 9395 | } |
| 9396 | } |
| 9397 | |
| 9398 | - (void)selectWordForReplacement |
| 9399 | { |
| 9400 | [self _internalBeginSelectionChange]; |
| 9401 | protect(_page)->extendSelectionForReplacement([weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 9402 | if (auto strongSelf = weakSelf.get()) |
| 9403 | [strongSelf _internalEndSelectionChange]; |
| 9404 | }); |
| 9405 | } |
| 9406 | |
| 9407 | #if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)(defined 0 && 0) |
| 9408 | - (void)replaceSelectionOffset:(NSInteger)selectionOffset length:(NSUInteger)length withAnnotatedString:(NSAttributedString *)annotatedString relativeReplacementRange:(NSRange)relativeReplacementRange |
| 9409 | { |
| 9410 | _textCheckingController->replaceRelativeToSelection(annotatedString, selectionOffset, length, relativeReplacementRange.location, relativeReplacementRange.length); |
| 9411 | } |
| 9412 | |
| 9413 | - (void)removeAnnotation:(NSAttributedStringKey)annotationName forSelectionOffset:(NSInteger)selectionOffset length:(NSUInteger)length |
| 9414 | { |
| 9415 | _textCheckingController->removeAnnotationRelativeToSelection(annotationName, selectionOffset, length); |
| 9416 | } |
| 9417 | #endif |
| 9418 | |
| 9419 | - (void)_updateChangedSelection |
| 9420 | { |
| 9421 | [self _updateChangedSelection:NO__objc_no]; |
| 9422 | } |
| 9423 | |
| 9424 | - (UIScrollView *)_scrollViewForScrollingNodeID:(std::optional<WebCore::ScrollingNodeID>)scrollingNodeID |
| 9425 | { |
| 9426 | if (WeakPtr coordinator = downcast<WebKit::RemoteScrollingCoordinatorProxyIOS>(_page->scrollingCoordinatorProxy())) |
| 9427 | return coordinator->scrollViewForScrollingNodeID(scrollingNodeID); |
| 9428 | |
| 9429 | return nilnullptr; |
| 9430 | } |
| 9431 | |
| 9432 | - (void)_updateChangedSelection:(BOOL)force |
| 9433 | { |
| 9434 | auto& editorState = protect(_page)->editorState(); |
| 9435 | if (!editorState.hasPostLayoutAndVisualData()) |
| 9436 | return; |
| 9437 | |
| 9438 | using enum WebKit::WKSelectionDrawingInfo::ComparisonResult; |
| 9439 | |
| 9440 | auto& postLayoutData = *editorState.postLayoutData; |
| 9441 | WebKit::WKSelectionDrawingInfo selectionDrawingInfo { editorState }; |
| 9442 | auto comparison = selectionDrawingInfo.compare(_lastSelectionDrawingInfo); |
| 9443 | if (force || comparison != EquivalentIncludingEnclosingLayer) { |
| 9444 | LOG_WITH_STREAM(Selection, stream << "_updateChangedSelection " << selectionDrawingInfo)((void)0); |
| 9445 | |
| 9446 | _cachedSelectedTextRange = nilnullptr; |
| 9447 | _lastSelectionDrawingInfo = selectionDrawingInfo; |
| 9448 | |
| 9449 | if (_textInteractionWrapper) { |
| 9450 | _markedText = editorState.hasComposition ? postLayoutData.markedText.createNSString().get() : @""; |
| 9451 | if (![_markedText length]) |
| 9452 | _isDeferringKeyEventsToInputMethod = NO__objc_no; |
| 9453 | RetainPtr containerView = [self _selectionContainerViewInternal]; |
| 9454 | [_textInteractionWrapper prepareToMoveSelectionContainer:containerView.get()]; |
| 9455 | if (comparison == VisuallyDistinct) |
| 9456 | [_textInteractionWrapper selectionChanged]; |
| 9457 | _lastSelectionChildScrollViewContentOffset = [containerView] -> std::optional<WebCore::IntPoint> { |
| 9458 | RetainPtr scrollView = [containerView _wk_parentScrollView]; |
| 9459 | if (is_objc<WKChildScrollView>(scrollView.get())) |
| 9460 | return WebCore::roundedIntPoint([scrollView contentOffset]); |
| 9461 | return { }; |
| 9462 | }(); |
| 9463 | _lastSiblingBeforeSelectionHighlight = [self _siblingBeforeSelectionHighlight]; |
| 9464 | _lastSelectionContainerViewOrigin = [containerView frame].origin; |
| 9465 | } |
| 9466 | |
| 9467 | _selectionNeedsUpdate = NO__objc_no; |
| 9468 | if (_shouldRestoreSelection) { |
| 9469 | [_textInteractionWrapper didEndScrollingOverflow]; |
| 9470 | _shouldRestoreSelection = NO__objc_no; |
| 9471 | } |
| 9472 | } else { |
| 9473 | if (_lastSiblingBeforeSelectionHighlight != [self _siblingBeforeSelectionHighlight]) |
| 9474 | [_textInteractionWrapper prepareToMoveSelectionContainer:self._selectionContainerViewInternal]; |
| 9475 | [self _updateSelectionViewsIfNeeded]; |
| 9476 | } |
| 9477 | |
| 9478 | if (postLayoutData.isStableStateUpdate && _needsDeferredEndScrollingSelectionUpdate && protect(_page)->inStableState()) { |
| 9479 | auto firstResponder = self.firstResponder; |
| 9480 | if ((!firstResponder || self == firstResponder) && !_suppressSelectionAssistantReasons) |
| 9481 | [_textInteractionWrapper activateSelection]; |
| 9482 | |
| 9483 | [_textInteractionWrapper didEndScrollingOverflow]; |
| 9484 | |
| 9485 | _needsDeferredEndScrollingSelectionUpdate = NO__objc_no; |
| 9486 | } |
| 9487 | } |
| 9488 | |
| 9489 | - (UIView *)_siblingBeforeSelectionHighlight |
| 9490 | { |
| 9491 | return [[_textInteractionWrapper selectionHighlightView] _wk_previousSibling]; |
| 9492 | } |
| 9493 | |
| 9494 | - (void)_updateSelectionViewsIfNeeded |
| 9495 | { |
| 9496 | RetainPtr selectionContainer = [self _selectionContainerViewInternal]; |
| 9497 | |
| 9498 | auto updateLastSelectionChildScrollViewContentOffset = [&] { |
| 9499 | if (_waitingForEditorStateAfterScrollingSelectionContainer) |
| 9500 | return NO__objc_no; |
| 9501 | |
| 9502 | RetainPtr scroller = [selectionContainer _wk_parentScrollView]; |
| 9503 | if (![_webView.get() _isInStableState:scroller.get()]) |
| 9504 | return NO__objc_no; |
| 9505 | |
| 9506 | if (!is_objc<WKChildScrollView>(scroller.get())) |
| 9507 | return NO__objc_no; |
| 9508 | |
| 9509 | auto contentOffset = WebCore::roundedIntPoint([scroller contentOffset]); |
| 9510 | if (_lastSelectionChildScrollViewContentOffset == contentOffset) |
| 9511 | return NO__objc_no; |
| 9512 | |
| 9513 | _lastSelectionChildScrollViewContentOffset = contentOffset; |
| 9514 | return YES__objc_yes; |
| 9515 | }; |
| 9516 | |
| 9517 | auto updateLastSelectionContainerViewOrigin = [&] -> BOOL { |
| 9518 | if (selectionContainer == self) |
| 9519 | return NO__objc_no; |
| 9520 | |
| 9521 | auto previousOrigin = std::exchange(_lastSelectionContainerViewOrigin, [selectionContainer frame].origin); |
| 9522 | return previousOrigin && !CGPointEqualToPoint__CGPointEqualToPoint(*previousOrigin, [selectionContainer frame].origin); |
| 9523 | }; |
| 9524 | |
| 9525 | BOOL needsUpdateDueToScrolling = updateLastSelectionChildScrollViewContentOffset(); |
| 9526 | BOOL needsUpdateDueToOriginChange = updateLastSelectionContainerViewOrigin(); |
| 9527 | |
| 9528 | if (needsUpdateDueToScrolling || needsUpdateDueToOriginChange) |
| 9529 | [_textInteractionWrapper setNeedsSelectionUpdate]; |
| 9530 | } |
| 9531 | |
| 9532 | - (BOOL)shouldAllowHidingSelectionCommands |
| 9533 | { |
| 9534 | ASSERT(_ignoreSelectionCommandFadeCount >= 0)((void)0); |
| 9535 | return !_ignoreSelectionCommandFadeCount; |
| 9536 | } |
| 9537 | |
| 9538 | - (BOOL)supportsTextSelectionWithCharacterGranularity |
| 9539 | { |
| 9540 | return YES__objc_yes; |
| 9541 | } |
| 9542 | |
| 9543 | #if ENABLE(REVEAL)(defined 1 && 1) |
| 9544 | - (void)requestRVItemInSelectedRangeWithCompletionHandler:(void(^)(RVItem *item))completionHandler |
| 9545 | { |
| 9546 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG225" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(9546, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 9547 | |
| 9548 | protect(_page)->requestRVItemInCurrentSelectedRange([completionHandler = makeBlockPtr(completionHandler), weakSelf = WeakObjCPtr<WKContentView>(self)](const WebKit::RevealItem& item) { |
| 9549 | auto strongSelf = weakSelf.get(); |
| 9550 | if (!strongSelf) |
| 9551 | return completionHandler(nilnullptr); |
| 9552 | |
| 9553 | completionHandler(protect(item.item())); |
| 9554 | }); |
| 9555 | } |
| 9556 | |
| 9557 | - (void)prepareSelectionForContextMenuWithLocationInView:(CGPoint)locationInView completionHandler:(void(^)(BOOL shouldPresentMenu, RVItem *item))completionHandler |
| 9558 | { |
| 9559 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG226" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(9559, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 9560 | |
| 9561 | [self _internalSelectTextForContextMenuWithLocationInView:locationInView completionHandler:[completionHandler = makeBlockPtr(completionHandler)](BOOL shouldPresentMenu, const WebKit::RevealItem& item) { |
| 9562 | completionHandler(shouldPresentMenu, protect(item.item())); |
| 9563 | }]; |
| 9564 | } |
| 9565 | |
| 9566 | - (void)_internalSelectTextForContextMenuWithLocationInView:(CGPoint)locationInView completionHandler:(void(^)(BOOL shouldPresentMenu, const WebKit::RevealItem& item))completionHandler |
| 9567 | { |
| 9568 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 9569 | _removeBackgroundData = std::nullopt; |
| 9570 | #endif |
| 9571 | |
| 9572 | protect(_page)->prepareSelectionForContextMenuWithLocationInView(WebCore::roundedIntPoint(locationInView), [weakSelf = WeakObjCPtr<WKContentView>(self), completionHandler = makeBlockPtr(completionHandler)](bool shouldPresentMenu, auto& item) { |
| 9573 | auto strongSelf = weakSelf.get(); |
| 9574 | if (!strongSelf) |
| 9575 | return completionHandler(false, { }); |
| 9576 | |
| 9577 | if (shouldPresentMenu && ![strongSelf shouldSuppressEditMenu]) |
| 9578 | [strongSelf->_textInteractionWrapper activateSelection]; |
| 9579 | |
| 9580 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 9581 | [strongSelf doAfterComputingImageAnalysisResultsForBackgroundRemoval:[completionHandler, shouldPresentMenu, item, weakSelf] { |
| 9582 | auto strongSelf = weakSelf.get(); |
| 9583 | if (!strongSelf) |
| 9584 | return completionHandler(false, { }); |
| 9585 | |
| 9586 | completionHandler(shouldPresentMenu, item); |
| 9587 | }]; |
| 9588 | #else |
| 9589 | completionHandler(shouldPresentMenu, item); |
| 9590 | #endif |
| 9591 | }); |
| 9592 | } |
| 9593 | #endif |
| 9594 | |
| 9595 | - (BOOL)hasHiddenContentEditable |
| 9596 | { |
| 9597 | return _suppressSelectionAssistantReasons.containsAny({ WebKit::SelectionIsTransparentOrFullyClipped, WebKit::FocusedElementIsTooSmall }); |
| 9598 | } |
| 9599 | |
| 9600 | - (BOOL)_shouldSuppressSelectionCommands |
| 9601 | { |
| 9602 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG227" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(9602, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 9603 | |
| 9604 | return self.shouldSuppressEditMenu; |
| 9605 | } |
| 9606 | |
| 9607 | - (void)_startSuppressingSelectionAssistantForReason:(WebKit::SuppressSelectionAssistantReason)reason |
| 9608 | { |
| 9609 | bool wasSuppressingSelectionAssistant = !!_suppressSelectionAssistantReasons; |
| 9610 | _suppressSelectionAssistantReasons.add(reason); |
| 9611 | |
| 9612 | if (!wasSuppressingSelectionAssistant) |
| 9613 | [_textInteractionWrapper deactivateSelection]; |
| 9614 | } |
| 9615 | |
| 9616 | - (void)_stopSuppressingSelectionAssistantForReason:(WebKit::SuppressSelectionAssistantReason)reason |
| 9617 | { |
| 9618 | bool wasSuppressingSelectionAssistant = !!_suppressSelectionAssistantReasons; |
| 9619 | _suppressSelectionAssistantReasons.remove(reason); |
| 9620 | |
| 9621 | if (wasSuppressingSelectionAssistant && !_suppressSelectionAssistantReasons) |
| 9622 | [_textInteractionWrapper activateSelection]; |
| 9623 | } |
| 9624 | |
| 9625 | - (BOOL)_isSuppressingSelectionAssistant |
| 9626 | { |
| 9627 | return !!_suppressSelectionAssistantReasons; |
| 9628 | } |
| 9629 | |
| 9630 | - (UIView <WKFormControl> *)dataListTextSuggestionsInputView |
| 9631 | { |
| 9632 | return _dataListTextSuggestionsInputView.get(); |
| 9633 | } |
| 9634 | |
| 9635 | - (NSArray<WKBETextSuggestionBETextSuggestion *> *)dataListTextSuggestions |
| 9636 | { |
| 9637 | return _dataListTextSuggestions.get(); |
| 9638 | } |
| 9639 | |
| 9640 | - (void)setDataListTextSuggestionsInputView:(UIView <WKFormControl> *)suggestionsInputView |
| 9641 | { |
| 9642 | if (_dataListTextSuggestionsInputView == suggestionsInputView) |
| 9643 | return; |
| 9644 | |
| 9645 | _dataListTextSuggestionsInputView = suggestionsInputView; |
| 9646 | |
| 9647 | if (![_formInputSession customInputView]) |
| 9648 | [self reloadInputViews]; |
| 9649 | } |
| 9650 | |
| 9651 | - (void)setDataListTextSuggestions:(NSArray<WKBETextSuggestionBETextSuggestion *> *)textSuggestions |
| 9652 | { |
| 9653 | if (textSuggestions == _dataListTextSuggestions || [textSuggestions isEqualToArray:_dataListTextSuggestions.get()]) |
| 9654 | return; |
| 9655 | |
| 9656 | _dataListTextSuggestions = textSuggestions; |
| 9657 | |
| 9658 | if (![_formInputSession suggestions].count) |
| 9659 | [self updateTextSuggestionsForInputDelegate]; |
| 9660 | } |
| 9661 | |
| 9662 | - (void)updateTextSuggestionsForInputDelegate |
| 9663 | { |
| 9664 | // Text suggestions vended from clients take precedence over text suggestions from a focused form control with a datalist. |
| 9665 | NSArray<UITextSuggestion *> *formInputSessionSuggestions = [_formInputSession suggestions]; |
| 9666 | if (formInputSessionSuggestions.count) { |
| 9667 | [self _provideUITextSuggestionsToInputDelegate:formInputSessionSuggestions]; |
| 9668 | return; |
| 9669 | } |
| 9670 | |
| 9671 | if ([_dataListTextSuggestions count]) { |
| 9672 | [self _provideSuggestionsToInputDelegate:_dataListTextSuggestions.get()]; |
| 9673 | return; |
| 9674 | } |
| 9675 | |
| 9676 | [self _provideSuggestionsToInputDelegate:nilnullptr]; |
| 9677 | } |
| 9678 | |
| 9679 | - (void)_provideSuggestionsToInputDelegate:(NSArray<WKBETextSuggestionBETextSuggestion *> *)suggestions |
| 9680 | { |
| 9681 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 9682 | if (self.shouldUseAsyncInteractions) { |
| 9683 | [protect(_asyncInputDelegate) textInput:self.asBETextInput setCandidateSuggestions:suggestions]; |
| 9684 | return; |
| 9685 | } |
| 9686 | |
| 9687 | RetainPtr uiSuggestions = [NSMutableArray arrayWithCapacity:suggestions.count]; |
| 9688 | for (UITextSuggestion *suggestion in suggestions) |
| 9689 | [uiSuggestions addObject:[UITextSuggestion textSuggestionWithInputText:suggestion.inputText]]; |
| 9690 | #else |
| 9691 | RetainPtr uiSuggestions = suggestions; |
| 9692 | #endif |
| 9693 | [(id<UITextInputSuggestionDelegate>)self.inputDelegate setSuggestions:uiSuggestions.get()]; |
| 9694 | } |
| 9695 | |
| 9696 | - (void)_provideUITextSuggestionsToInputDelegate:(NSArray<UITextSuggestion *> *)suggestions |
| 9697 | { |
| 9698 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 9699 | if (self.shouldUseAsyncInteractions) { |
| 9700 | RetainPtr suggestionsForDelegate = [NSMutableArray arrayWithCapacity:suggestions.count]; |
| 9701 | for (UITextSuggestion *suggestion in suggestions) |
| 9702 | [suggestionsForDelegate addObject:adoptNS([[WKBETextSuggestionBETextSuggestion alloc] _initWithUIKitTextSuggestion:suggestion]).get()]; |
| 9703 | [protect(_asyncInputDelegate) textInput:self.asBETextInput setCandidateSuggestions:suggestionsForDelegate.get()]; |
| 9704 | return; |
| 9705 | } |
| 9706 | #endif // USE(BROWSERENGINEKIT) |
| 9707 | |
| 9708 | [(id<UITextInputSuggestionDelegate>)self.inputDelegate setSuggestions:suggestions]; |
| 9709 | } |
| 9710 | |
| 9711 | - (void)_showPlaybackTargetPicker:(BOOL)hasVideo fromRect:(const WebCore::IntRect&)elementRect routeSharingPolicy:(WebCore::RouteSharingPolicy)routeSharingPolicy routingContextUID:(NSString *)routingContextUID |
| 9712 | { |
| 9713 | #if ENABLE(AIRPLAY_PICKER)(defined 1 && 1) |
| 9714 | if (!_airPlayRoutePicker) |
| 9715 | _airPlayRoutePicker = adoptNS([[WKAirPlayRoutePicker alloc] init]); |
| 9716 | [_airPlayRoutePicker showFromView:self routeSharingPolicy:routeSharingPolicy routingContextUID:routingContextUID hasVideo:hasVideo]; |
| 9717 | #endif |
| 9718 | } |
| 9719 | |
| 9720 | - (void)_showRunOpenPanel:(API::OpenPanelParameters*)parameters frameInfo:(const WebKit::FrameInfoData&)frameInfo resultListener:(WebKit::WebOpenPanelResultListenerProxy*)listener |
| 9721 | { |
| 9722 | ASSERT(!_fileUploadPanel)((void)0); |
| 9723 | if (_fileUploadPanel) |
| 9724 | return; |
| 9725 | |
| 9726 | _frameInfoForFileUploadPanel = frameInfo; |
| 9727 | _fileUploadPanel = adoptNS([[WKFileUploadPanel alloc] initWithView:self]); |
| 9728 | [_fileUploadPanel setDelegate:self]; |
| 9729 | [_fileUploadPanel presentWithParameters:parameters resultListener:listener]; |
| 9730 | } |
| 9731 | |
| 9732 | - (void)fileUploadPanelDidDismiss:(WKFileUploadPanel *)fileUploadPanel |
| 9733 | { |
| 9734 | ASSERT(_fileUploadPanel.get() == fileUploadPanel)((void)0); |
| 9735 | |
| 9736 | if ([self window] && ![[self window] firstResponder]) |
| 9737 | [self becomeFirstResponder]; |
| 9738 | |
| 9739 | [_fileUploadPanel setDelegate:nilnullptr]; |
| 9740 | _fileUploadPanel = nilnullptr; |
| 9741 | } |
| 9742 | |
| 9743 | - (BOOL)fileUploadPanelDestinationIsManaged:(WKFileUploadPanel *)fileUploadPanel |
| 9744 | { |
| 9745 | ASSERT(_fileUploadPanel.get() == fileUploadPanel)((void)0); |
| 9746 | |
| 9747 | auto webView = _webView.get(); |
| 9748 | id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([webView UIDelegate]); |
| 9749 | return [uiDelegate respondsToSelector:@selector(_webView:fileUploadPanelContentIsManagedWithInitiatingFrame:)] |
| 9750 | && [uiDelegate _webView:webView.get() fileUploadPanelContentIsManagedWithInitiatingFrame:_frameInfoForFileUploadPanel ? wrapper(API::FrameInfo::create(*std::exchange(_frameInfoForFileUploadPanel, std::nullopt))).get() : nilnullptr]; |
| 9751 | } |
| 9752 | |
| 9753 | #if HAVE(PHOTOS_UI)(defined 1 && 1) |
| 9754 | - (BOOL)fileUploadPanelPhotoPickerPrefersOriginalImageFormat:(WKFileUploadPanel *)fileUploadPanel |
| 9755 | { |
| 9756 | ASSERT(_fileUploadPanel.get() == fileUploadPanel)((void)0); |
| 9757 | return protect(_page->preferences())->photoPickerPrefersOriginalImageFormat(); |
| 9758 | } |
| 9759 | #endif |
| 9760 | |
| 9761 | - (void)_showShareSheet:(const WebCore::ShareDataWithParsedURL&)data inRect:(std::optional<WebCore::FloatRect>)rect completionHandler:(CompletionHandler<void(bool)>&&)completionHandler |
| 9762 | { |
| 9763 | #if HAVE(SHARE_SHEET_UI)(defined 1 && 1) |
| 9764 | if (_shareSheet) |
| 9765 | [_shareSheet dismissIfNeededWithReason:WebKit::PickerDismissalReason::ResetState]; |
| 9766 | |
| 9767 | _shareSheet = adoptNS([[WKShareSheet alloc] initWithView:self.webView]); |
| 9768 | [_shareSheet setDelegate:self]; |
| 9769 | |
| 9770 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 9771 | if (!rect) { |
| 9772 | if (auto lastLocation = [_mouseInteraction lastLocation]) { |
| 9773 | auto hoverLocationInWebView = [self convertPoint:*lastLocation toView:self.webView]; |
| 9774 | rect = WebCore::FloatRect(hoverLocationInWebView.x, hoverLocationInWebView.y, 1, 1); |
| 9775 | } |
| 9776 | } |
| 9777 | #endif |
| 9778 | |
| 9779 | [_shareSheet presentWithParameters:data inRect:rect completionHandler:WTF::move(completionHandler)]; |
| 9780 | #endif // HAVE(SHARE_SHEET_UI) |
| 9781 | } |
| 9782 | |
| 9783 | #if HAVE(SHARE_SHEET_UI)(defined 1 && 1) |
| 9784 | |
| 9785 | - (void)shareSheetDidDismiss:(WKShareSheet *)shareSheet |
| 9786 | { |
| 9787 | ASSERT(_shareSheet == shareSheet)((void)0); |
| 9788 | |
| 9789 | [_shareSheet setDelegate:nilnullptr]; |
| 9790 | _shareSheet = nilnullptr; |
| 9791 | } |
| 9792 | |
| 9793 | - (void)shareSheet:(WKShareSheet *)shareSheet willShowActivityItems:(NSArray *)activityItems |
| 9794 | { |
| 9795 | ASSERT(_shareSheet == shareSheet)((void)0); |
| 9796 | |
| 9797 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 9798 | if ([uiDelegate respondsToSelector:@selector(_webView:willShareActivityItems:)]) |
| 9799 | [uiDelegate _webView:self.webView willShareActivityItems:activityItems]; |
| 9800 | } |
| 9801 | |
| 9802 | #endif // HAVE(SHARE_SHEET_UI) |
| 9803 | |
| 9804 | #if ENABLE(WEB_AUTHN)(defined 1 && 1) |
| 9805 | - (void)_showDigitalCredentialsPicker:(const WebCore::DigitalCredentialsRequestData&)requestData completionHandler:(WTF::CompletionHandler<void(Expected<WebCore::DigitalCredentialsResponseData, WebCore::ExceptionData>&&)>&&)completionHandler |
| 9806 | { |
| 9807 | _digitalCredentialsPicker = adoptNS([[WKDigitalCredentialsPicker alloc] initWithView:self.webView page:_page.get()]); |
| 9808 | [_digitalCredentialsPicker presentWithRequestData:requestData completionHandler:WTF::move(completionHandler)]; |
| 9809 | } |
| 9810 | |
| 9811 | - (void)_dismissDigitalCredentialsPicker:(WTF::CompletionHandler<void(bool)>&&)completionHandler |
| 9812 | { |
| 9813 | if (!_digitalCredentialsPicker) { |
| 9814 | LOG(DigitalCredentials, "Digital credentials picker is not presented.")((void)0); |
| 9815 | completionHandler(false); |
| 9816 | return; |
| 9817 | } |
| 9818 | [_digitalCredentialsPicker dismissWithCompletionHandler:WTF::move(completionHandler)]; |
| 9819 | } |
| 9820 | #endif // ENABLE(WEB_AUTHN) |
| 9821 | |
| 9822 | - (void)_showContactPicker:(const WebCore::ContactsRequestData&)requestData completionHandler:(WTF::CompletionHandler<void(std::optional<Vector<WebCore::ContactInfo>>&&)>&&)completionHandler |
| 9823 | { |
| 9824 | #if HAVE(CONTACTSUI)(defined 1 && 1) |
| 9825 | _contactPicker = adoptNS([[WKContactPicker alloc] initWithView:self.webView]); |
| 9826 | [_contactPicker setDelegate:self]; |
| 9827 | [_contactPicker presentWithRequestData:requestData completionHandler:WTF::move(completionHandler)]; |
| 9828 | #else |
| 9829 | completionHandler(std::nullopt); |
| 9830 | #endif |
| 9831 | } |
| 9832 | |
| 9833 | #if HAVE(CONTACTSUI)(defined 1 && 1) |
| 9834 | - (void)contactPickerDidPresent:(WKContactPicker *)contactPicker |
| 9835 | { |
| 9836 | ASSERT(_contactPicker == contactPicker)((void)0); |
| 9837 | |
| 9838 | [_webView.get() _didPresentContactPicker]; |
| 9839 | } |
| 9840 | |
| 9841 | - (void)contactPickerDidDismiss:(WKContactPicker *)contactPicker |
| 9842 | { |
| 9843 | ASSERT(_contactPicker == contactPicker)((void)0); |
| 9844 | |
| 9845 | [_contactPicker setDelegate:nilnullptr]; |
| 9846 | _contactPicker = nilnullptr; |
| 9847 | |
| 9848 | [_webView.get() _didDismissContactPicker]; |
| 9849 | } |
| 9850 | #endif |
| 9851 | |
| 9852 | - (void)dismissPickersIfNeededWithReason:(WebKit::PickerDismissalReason)reason |
| 9853 | { |
| 9854 | if ([_fileUploadPanel dismissIfNeededWithReason:reason]) |
| 9855 | _fileUploadPanel = nilnullptr; |
| 9856 | |
| 9857 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) && !PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV ) |
| 9858 | if ([_shareSheet dismissIfNeededWithReason:reason]) |
| 9859 | _shareSheet = nilnullptr; |
| 9860 | #endif |
| 9861 | |
| 9862 | #if HAVE(CONTACTSUI)(defined 1 && 1) |
| 9863 | if ([_contactPicker dismissIfNeededWithReason:reason]) |
| 9864 | _contactPicker = nilnullptr; |
| 9865 | #endif |
| 9866 | } |
| 9867 | |
| 9868 | #if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 9869 | static String fallbackLabelTextForUnlabeledInputFieldInZoomedFormControls(WebCore::InputMode inputMode, WebKit::InputType elementType) |
| 9870 | { |
| 9871 | bool isPasswordField = false; |
| 9872 | |
| 9873 | auto elementTypeIsAnyOf = [elementType](std::initializer_list<WebKit::InputType>&& elementTypes) { |
| 9874 | return std::ranges::find(elementTypes, elementType) != elementTypes.end(); |
| 9875 | }; |
| 9876 | |
| 9877 | // If unspecified, try to infer the input mode from the input type |
| 9878 | if (inputMode == WebCore::InputMode::Unspecified) { |
| 9879 | if (elementTypeIsAnyOf({ WebKit::InputType::ContentEditable, WebKit::InputType::Text, WebKit::InputType::TextArea })) |
| 9880 | inputMode = WebCore::InputMode::Text; |
| 9881 | if (elementTypeIsAnyOf({ WebKit::InputType::URL })) |
| 9882 | inputMode = WebCore::InputMode::Url; |
| 9883 | if (elementTypeIsAnyOf({ WebKit::InputType::Number, WebKit::InputType::NumberPad })) |
| 9884 | inputMode = WebCore::InputMode::Numeric; |
| 9885 | if (elementTypeIsAnyOf({ WebKit::InputType::Search })) |
| 9886 | inputMode = WebCore::InputMode::Search; |
| 9887 | if (elementTypeIsAnyOf({ WebKit::InputType::Email })) |
| 9888 | inputMode = WebCore::InputMode::Email; |
| 9889 | if (elementTypeIsAnyOf({ WebKit::InputType::Phone })) |
| 9890 | inputMode = WebCore::InputMode::Telephone; |
| 9891 | if (elementTypeIsAnyOf({ WebKit::InputType::Password })) |
| 9892 | isPasswordField = true; |
| 9893 | } |
| 9894 | |
| 9895 | if (isPasswordField) |
| 9896 | return WEB_UI_STRING("Password", "Fallback label text for unlabeled password field.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Password" ""))); |
| 9897 | |
| 9898 | switch (inputMode) { |
| 9899 | case WebCore::InputMode::Telephone: |
| 9900 | return WEB_UI_STRING("Phone number", "Fallback label text for unlabeled telephone number input field.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Phone number" ""))); |
| 9901 | case WebCore::InputMode::Url: |
| 9902 | return WEB_UI_STRING("URL", "Fallback label text for unlabeled URL input field.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "URL" ""))); |
| 9903 | case WebCore::InputMode::Email: |
| 9904 | return WEB_UI_STRING("Email address", "Fallback label text for unlabeled email address field.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Email address" ""))); |
| 9905 | case WebCore::InputMode::Numeric: |
| 9906 | case WebCore::InputMode::Decimal: |
| 9907 | return WEB_UI_STRING("Enter a number", "Fallback label text for unlabeled numeric field.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Enter a number" ""))); |
| 9908 | case WebCore::InputMode::Search: |
| 9909 | return WEB_UI_STRING("Search", "Fallback label text for unlabeled search field")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Search" ""))); |
| 9910 | case WebCore::InputMode::Unspecified: |
| 9911 | case WebCore::InputMode::None: |
| 9912 | case WebCore::InputMode::Text: |
| 9913 | return WEB_UI_STRING("Enter text", "Fallback label text for unlabeled text field.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Enter text" ""))); |
| 9914 | } |
| 9915 | } |
| 9916 | #endif |
| 9917 | |
| 9918 | - (NSString *)inputLabelText |
| 9919 | { |
| 9920 | if (!_focusedElementInformation.label.isEmpty()) |
| 9921 | return _focusedElementInformation.label.createNSString().autorelease(); |
| 9922 | |
| 9923 | if (!_focusedElementInformation.ariaLabel.isEmpty()) |
| 9924 | return _focusedElementInformation.ariaLabel.createNSString().autorelease(); |
| 9925 | |
| 9926 | if (!_focusedElementInformation.title.isEmpty()) |
| 9927 | return _focusedElementInformation.title.createNSString().autorelease(); |
| 9928 | |
| 9929 | #if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 9930 | if (_focusedElementInformation.placeholder.isEmpty()) |
| 9931 | return fallbackLabelTextForUnlabeledInputFieldInZoomedFormControls(_focusedElementInformation.inputMode, _focusedElementInformation.elementType).createNSString().autorelease(); |
| 9932 | #endif |
| 9933 | |
| 9934 | return _focusedElementInformation.placeholder.createNSString().autorelease(); |
| 9935 | } |
| 9936 | |
| 9937 | #pragma mark - UITextInputMultiDocument |
| 9938 | |
| 9939 | - (BOOL)_restoreFocusWithToken:(id <NSCopying, NSSecureCoding>)token |
| 9940 | { |
| 9941 | if (_focusStateStack.isEmpty()) { |
| 9942 | ASSERT_NOT_REACHED()((void)0); |
| 9943 | return NO__objc_no; |
| 9944 | } |
| 9945 | |
| 9946 | if (_focusStateStack.takeLast()) |
| 9947 | [_webView.get() _decrementFocusPreservationCount]; |
| 9948 | |
| 9949 | // FIXME: Our current behavior in -_restoreFocusWithToken: does not force the web view to become first responder |
| 9950 | // by refocusing the currently focused element. As such, we return NO here so that UIKit will tell WKContentView |
| 9951 | // to become first responder in the future. |
| 9952 | return NO__objc_no; |
| 9953 | } |
| 9954 | |
| 9955 | - (void)startRelinquishingFirstResponderToFocusedElement |
| 9956 | { |
| 9957 | if (_isRelinquishingFirstResponderToFocusedElement) |
| 9958 | return; |
| 9959 | |
| 9960 | _isRelinquishingFirstResponderToFocusedElement = YES__objc_yes; |
| 9961 | [_webView.get() _incrementFocusPreservationCount]; |
| 9962 | } |
| 9963 | |
| 9964 | - (void)stopRelinquishingFirstResponderToFocusedElement |
| 9965 | { |
| 9966 | if (!_isRelinquishingFirstResponderToFocusedElement) |
| 9967 | return; |
| 9968 | |
| 9969 | _isRelinquishingFirstResponderToFocusedElement = NO__objc_no; |
| 9970 | [_webView.get() _decrementFocusPreservationCount]; |
| 9971 | } |
| 9972 | |
| 9973 | - (void)_preserveFocusWithToken:(id <NSCopying, NSSecureCoding>)token destructively:(BOOL)destructively |
| 9974 | { |
| 9975 | if (!_inputPeripheral) { |
| 9976 | [_webView.get() _incrementFocusPreservationCount]; |
| 9977 | _focusStateStack.append(true); |
| 9978 | } else |
| 9979 | _focusStateStack.append(false); |
| 9980 | } |
| 9981 | |
| 9982 | - (BOOL)_shouldIgnoreTouchEvent:(UIEvent *)event |
| 9983 | { |
| 9984 | _touchEventsCanPreventNativeGestures = YES__objc_yes; |
| 9985 | |
| 9986 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 9987 | if ([_imageAnalysisInteraction interactableItemExistsAtPoint:[_touchEventGestureRecognizer locationInView:self]]) |
| 9988 | return YES__objc_yes; |
| 9989 | #endif |
| 9990 | |
| 9991 | return [self gestureRecognizer:_touchEventGestureRecognizer.get() isInterruptingMomentumScrollingWithEvent:event]; |
| 9992 | } |
| 9993 | |
| 9994 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer isInterruptingMomentumScrollingWithEvent:(UIEvent *)event |
| 9995 | { |
| 9996 | NSSet<UITouch *> *touches = [event touchesForGestureRecognizer:gestureRecognizer]; |
| 9997 | for (UITouch *touch in touches) { |
| 9998 | if (dynamic_objc_cast<UIScrollView>(touch.view)._wk_isInterruptingDeceleration) |
| 9999 | return YES__objc_yes; |
| 10000 | } |
| 10001 | return self._scroller._wk_isInterruptingDeceleration; |
| 10002 | } |
| 10003 | |
| 10004 | #pragma mark - Implementation of WKActionSheetAssistantDelegate. |
| 10005 | |
| 10006 | - (std::optional<WebKit::InteractionInformationAtPosition>)positionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10007 | { |
| 10008 | WebKit::InteractionInformationRequest request(_positionInformation.request.point); |
| 10009 | request.includeSnapshot = true; |
| 10010 | request.includeLinkIndicator = assistant.needsLinkIndicator; |
| 10011 | request.linkIndicatorShouldHaveLegacyMargins = !self._shouldUseContextMenus; |
| 10012 | request.gatherAnimations = [self.webView _allowAnimationControls]; |
| 10013 | |
| 10014 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 10015 | return std::nullopt; |
| 10016 | |
| 10017 | return _positionInformation; |
| 10018 | } |
| 10019 | |
| 10020 | - (void)updatePositionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10021 | { |
| 10022 | _hasValidPositionInformation = NO__objc_no; |
| 10023 | WebKit::InteractionInformationRequest request(_positionInformation.request.point); |
| 10024 | request.includeSnapshot = true; |
| 10025 | request.includeLinkIndicator = assistant.needsLinkIndicator; |
| 10026 | request.linkIndicatorShouldHaveLegacyMargins = !self._shouldUseContextMenus; |
| 10027 | request.gatherAnimations = [self.webView _allowAnimationControls]; |
| 10028 | |
| 10029 | [self requestAsynchronousPositionInformationUpdate:request]; |
| 10030 | } |
| 10031 | |
| 10032 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action |
| 10033 | { |
| 10034 | if (action == WebKit::SheetAction::Copy && [self _tryToCopyLinkURLFromPlugin]) |
| 10035 | return; |
| 10036 | |
| 10037 | protect(_page)->performActionOnElement((uint32_t)action); |
| 10038 | } |
| 10039 | |
| 10040 | - (void)_actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action onElements:(Vector<WebCore::ElementContext>&&)elements |
| 10041 | { |
| 10042 | protect(_page)->performActionOnElements((uint32_t)action, WTF::move(elements)); |
| 10043 | } |
| 10044 | |
| 10045 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location |
| 10046 | { |
| 10047 | [self _attemptSyntheticClickAtLocation:location modifierFlags:0]; |
| 10048 | } |
| 10049 | |
| 10050 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant shareElementWithURL:(NSURL *)url rect:(CGRect)boundingRect |
| 10051 | { |
| 10052 | WebCore::ShareDataWithParsedURL shareData; |
| 10053 | shareData.url = { url }; |
| 10054 | shareData.originator = WebCore::ShareDataOriginator::User; |
| 10055 | [self _showShareSheet:shareData inRect: { [self convertRect:boundingRect toView:self.webView] } completionHandler:nilnullptr]; |
| 10056 | } |
| 10057 | |
| 10058 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant shareElementWithImage:(UIImage *)image rect:(CGRect)boundingRect |
| 10059 | { |
| 10060 | WebCore::ShareDataWithParsedURL shareData; |
| 10061 | RetainPtr fileName = adoptNS([[NSString alloc] initWithFormat:@"%@.png", (NSString*)WEB_UI_STRING("Shared Image", "Default name for the file created for a shared image with no explicit name.")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString ("" "Shared Image" ""))).createNSString().get()]); |
| 10062 | shareData.files = { { fileName.get(), WebCore::SharedBuffer::create(protect(UIImagePNGRepresentation(image))) } }; |
| 10063 | shareData.originator = WebCore::ShareDataOriginator::User; |
| 10064 | [self _showShareSheet:shareData inRect: { [self convertRect:boundingRect toView:self.webView] } completionHandler:nilnullptr]; |
| 10065 | } |
| 10066 | |
| 10067 | #if HAVE(APP_LINKS)(defined 1 && 1) |
| 10068 | - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element |
| 10069 | { |
| 10070 | return _page->uiClient().shouldIncludeAppLinkActionsForElement(element); |
| 10071 | } |
| 10072 | #endif |
| 10073 | |
| 10074 | - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant showCustomSheetForElement:(_WKActivatedElementInfo *)element |
| 10075 | { |
| 10076 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 10077 | |
| 10078 | if ([uiDelegate respondsToSelector:@selector(_webView:showCustomSheetForElement:)]) { |
| 10079 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 10080 | if ([uiDelegate _webView:self.webView showCustomSheetForElement:element]) { |
| 10081 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 10082 | BOOL shouldCancelAllTouches = !_dragDropInteractionState.dragSession(); |
| 10083 | #else |
| 10084 | BOOL shouldCancelAllTouches = YES__objc_yes; |
| 10085 | #endif |
| 10086 | |
| 10087 | // Prevent tap-and-hold and panning. |
| 10088 | if (shouldCancelAllTouches) |
| 10089 | [UIApplication.sharedApplication _cancelAllTouches]; |
| 10090 | |
| 10091 | return YES__objc_yes; |
| 10092 | } |
| 10093 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 10094 | } |
| 10095 | |
| 10096 | return NO__objc_no; |
| 10097 | } |
| 10098 | |
| 10099 | - (BOOL)_tryToCopyLinkURLFromPlugin |
| 10100 | { |
| 10101 | if (!_positionInformation.isLink || !_positionInformation.isInPlugin) |
| 10102 | return NO__objc_no; |
| 10103 | |
| 10104 | RetainPtr urlToCopy = _positionInformation.url.createNSURL(); |
| 10105 | if (!urlToCopy) |
| 10106 | return NO__objc_no; |
| 10107 | |
| 10108 | [UIPasteboard _performAsDataOwner:[_webView.get() _effectiveDataOwner:self._dataOwnerForCopy] block:^{ |
| 10109 | UIPasteboard.generalPasteboard.URL = urlToCopy.get(); |
| 10110 | }]; |
| 10111 | return YES__objc_yes; |
| 10112 | } |
| 10113 | |
| 10114 | // FIXME: Likely we can remove this special case for watchOS. |
| 10115 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 10116 | - (CGRect)unoccludedWindowBoundsForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10117 | { |
| 10118 | UIEdgeInsets contentInset = [[_webView.get() scrollView] adjustedContentInset]; |
| 10119 | CGRect rect = UIEdgeInsetsInsetRect([_webView.get() bounds], contentInset); |
| 10120 | return [_webView.get() convertRect:rect toView:[self window]]; |
| 10121 | } |
| 10122 | #endif |
| 10123 | |
| 10124 | - (RetainPtr<NSArray>)actionSheetAssistant:(WKActionSheetAssistant *)assistant decideActionsForElement:(_WKActivatedElementInfo *)element defaultActions:(RetainPtr<NSArray>)defaultActions |
| 10125 | { |
| 10126 | return _page->uiClient().actionsForElement(element, WTF::move(defaultActions)); |
| 10127 | } |
| 10128 | |
| 10129 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant willStartInteractionWithElement:(_WKActivatedElementInfo *)element |
| 10130 | { |
| 10131 | protect(_page)->startInteractionWithPositionInformation(_positionInformation); |
| 10132 | } |
| 10133 | |
| 10134 | - (void)actionSheetAssistantDidStopInteraction:(WKActionSheetAssistant *)assistant |
| 10135 | { |
| 10136 | protect(_page)->stopInteraction(); |
| 10137 | } |
| 10138 | |
| 10139 | - (NSDictionary *)dataDetectionContextForPositionInformation:(const WebKit::InteractionInformationAtPosition&)positionInformation |
| 10140 | { |
| 10141 | RetainPtr<NSMutableDictionary> context; |
| 10142 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 10143 | if ([uiDelegate respondsToSelector:@selector(_dataDetectionContextForWebView:)]) |
| 10144 | context = adoptNS([[uiDelegate _dataDetectionContextForWebView:self.webView] mutableCopy]); |
| 10145 | |
| 10146 | if (!context) |
| 10147 | context = adoptNS([[NSMutableDictionary alloc] init]); |
| 10148 | |
| 10149 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 10150 | if (!positionInformation.textBefore.isEmpty()) |
| 10151 | context.get()[PAL::get_DataDetectorsUI_kDataDetectorsLeadingTextSingleton()] = positionInformation.textBefore.createNSString().get(); |
| 10152 | if (!positionInformation.textAfter.isEmpty()) |
| 10153 | context.get()[PAL::get_DataDetectorsUI_kDataDetectorsTrailingTextSingleton()] = positionInformation.textAfter.createNSString().get(); |
| 10154 | |
| 10155 | auto canShowPreview = ^{ |
| 10156 | if (!positionInformation.url.createNSURL().get().iTunesStoreURL) |
| 10157 | return YES__objc_yes; |
| 10158 | if (!_page->websiteDataStore().isPersistent()) |
| 10159 | return NO__objc_no; |
| 10160 | #if ENABLE(ADVANCED_PRIVACY_PROTECTIONS)(defined 1 && 1) |
| 10161 | if (_page->advancedPrivacyProtectionsPolicies().contains(WebCore::AdvancedPrivacyProtections::BaselineProtections)) |
| 10162 | return NO__objc_no; |
| 10163 | #endif |
| 10164 | return YES__objc_yes; |
| 10165 | }(); |
| 10166 | |
| 10167 | if (!canShowPreview) |
| 10168 | context.get()[PAL::get_DataDetectorsUI_kDDContextMenuWantsPreviewKeySingleton()] = @NO__objc_no; |
| 10169 | |
| 10170 | CGRect sourceRect; |
| 10171 | if (positionInformation.isLink && positionInformation.textIndicator) |
| 10172 | sourceRect = positionInformation.textIndicator->textBoundingRectInRootViewCoordinates(); |
| 10173 | else if (!positionInformation.dataDetectorBounds.isEmpty()) |
| 10174 | sourceRect = positionInformation.dataDetectorBounds; |
| 10175 | else |
| 10176 | sourceRect = positionInformation.bounds; |
| 10177 | |
| 10178 | CGRect frameInContainerViewCoordinates = [self convertRect:sourceRect toView:self.containerForContextMenuHintPreviews]; |
| 10179 | return [PAL::getDDContextMenuActionClassSingleton() updateContext:context.get() withSourceRect:frameInContainerViewCoordinates]; |
| 10180 | #else |
| 10181 | return context.autorelease(); |
| 10182 | #endif |
| 10183 | } |
| 10184 | |
| 10185 | - (NSDictionary *)dataDetectionContextForActionSheetAssistant:(WKActionSheetAssistant *)assistant positionInformation:(const WebKit::InteractionInformationAtPosition&)positionInformation |
| 10186 | { |
| 10187 | return [self dataDetectionContextForPositionInformation:positionInformation]; |
| 10188 | } |
| 10189 | |
| 10190 | - (NSString *)selectedTextForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10191 | { |
| 10192 | return [self selectedText]; |
| 10193 | } |
| 10194 | |
| 10195 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant getAlternateURLForImage:(UIImage *)image completion:(void (^)(NSURL *alternateURL, NSDictionary *userInfo))completion |
| 10196 | { |
| 10197 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 10198 | if ([uiDelegate respondsToSelector:@selector(_webView:getAlternateURLFromImage:completionHandler:)]) { |
| 10199 | [uiDelegate _webView:self.webView getAlternateURLFromImage:image completionHandler:^(NSURL *alternateURL, NSDictionary *userInfo) { |
| 10200 | completion(alternateURL, userInfo); |
| 10201 | }]; |
| 10202 | } else |
| 10203 | completion(nilnullptr, nilnullptr); |
| 10204 | } |
| 10205 | |
| 10206 | - (NSArray<UIMenuElement *> *)additionalMediaControlsContextMenuItemsForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10207 | { |
| 10208 | #if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION) |
| 10209 | if (self.webView.fullscreenState == WKFullscreenStateInFullscreen) |
| 10210 | return @[ [self.webView fullScreenWindowSceneDimmingAction] ]; |
| 10211 | #endif |
| 10212 | return @[ ]; |
| 10213 | } |
| 10214 | |
| 10215 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 10216 | |
| 10217 | - (UITargetedPreview *)createTargetedContextMenuHintForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10218 | { |
| 10219 | return [self _createTargetedContextMenuHintPreviewIfPossible]; |
| 10220 | } |
| 10221 | |
| 10222 | - (void)removeContextMenuViewIfPossibleForActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| 10223 | { |
| 10224 | [self _removeContextMenuHintContainerIfPossible]; |
| 10225 | } |
| 10226 | |
| 10227 | - (void)actionSheetAssistantDidShowContextMenu:(WKActionSheetAssistant *)assistant |
| 10228 | { |
| 10229 | [_webView.get() _didShowContextMenu]; |
| 10230 | } |
| 10231 | |
| 10232 | - (void)actionSheetAssistantDidDismissContextMenu:(WKActionSheetAssistant *)assistant |
| 10233 | { |
| 10234 | [_webView.get() _didDismissContextMenu]; |
| 10235 | } |
| 10236 | |
| 10237 | - (void)_targetedPreviewContainerDidRemoveLastSubview:(WKTargetedPreviewContainer *)containerView |
| 10238 | { |
| 10239 | if (_contextMenuHintContainerView == containerView) |
| 10240 | [self _removeContextMenuHintContainerIfPossible]; |
| 10241 | } |
| 10242 | |
| 10243 | #endif // USE(UICONTEXTMENU) |
| 10244 | |
| 10245 | - (BOOL)_shouldUseContextMenus |
| 10246 | { |
| 10247 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 10248 | return linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::HasUIContextMenuInteraction); |
| 10249 | #endif |
| 10250 | return NO__objc_no; |
| 10251 | } |
| 10252 | |
| 10253 | - (BOOL)_shouldUseContextMenusForFormControls |
| 10254 | { |
| 10255 | return self._formControlRefreshEnabled && self._shouldUseContextMenus; |
| 10256 | } |
| 10257 | |
| 10258 | - (BOOL)_shouldAvoidResizingWhenInputViewBoundsChange |
| 10259 | { |
| 10260 | return _focusedElementInformation.shouldAvoidResizingWhenInputViewBoundsChange; |
| 10261 | } |
| 10262 | |
| 10263 | - (BOOL)_shouldAvoidScrollingWhenFocusedContentIsVisible |
| 10264 | { |
| 10265 | return _focusedElementInformation.shouldAvoidScrollingWhenFocusedContentIsVisible; |
| 10266 | } |
| 10267 | |
| 10268 | - (BOOL)_shouldUseLegacySelectPopoverDismissalBehavior |
| 10269 | { |
| 10270 | if (PAL::currentUserInterfaceIdiomIsSmallScreen()) |
| 10271 | return NO__objc_no; |
| 10272 | |
| 10273 | if (_focusedElementInformation.elementType != WebKit::InputType::Select) |
| 10274 | return NO__objc_no; |
| 10275 | |
| 10276 | if (!_focusedElementInformation.shouldUseLegacySelectPopoverDismissalBehaviorInDataActivation) |
| 10277 | return NO__objc_no; |
| 10278 | |
| 10279 | return WTF::IOSApplication::isDataActivation(); |
| 10280 | } |
| 10281 | |
| 10282 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 10283 | |
| 10284 | - (BOOL)shouldDeferGestureDueToImageAnalysis:(UIGestureRecognizer *)gesture |
| 10285 | { |
| 10286 | return gesture._wk_isTextInteractionLoupeGesture || gesture._wk_isTapAndAHalf; |
| 10287 | } |
| 10288 | |
| 10289 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 10290 | |
| 10291 | static WebCore::DataOwnerType coreDataOwnerType(_UIDataOwner platformType) |
| 10292 | { |
| 10293 | switch (platformType) { |
| 10294 | case _UIDataOwnerUser: |
| 10295 | return WebCore::DataOwnerType::User; |
| 10296 | case _UIDataOwnerEnterprise: |
| 10297 | return WebCore::DataOwnerType::Enterprise; |
| 10298 | case _UIDataOwnerShared: |
| 10299 | return WebCore::DataOwnerType::Shared; |
| 10300 | case _UIDataOwnerUndefined: |
| 10301 | return WebCore::DataOwnerType::Undefined; |
| 10302 | } |
| 10303 | ASSERT_NOT_REACHED()((void)0); |
| 10304 | return WebCore::DataOwnerType::Undefined; |
| 10305 | } |
| 10306 | |
| 10307 | - (WebCore::DataOwnerType)_dataOwnerForPasteboard:(WebKit::PasteboardAccessIntent)intent |
| 10308 | { |
| 10309 | auto clientSuppliedDataOwner = intent == WebKit::PasteboardAccessIntent::Read ? self._dataOwnerForPaste : self._dataOwnerForCopy; |
| 10310 | return coreDataOwnerType([_webView.get() _effectiveDataOwner:clientSuppliedDataOwner]); |
| 10311 | } |
| 10312 | |
| 10313 | - (RetainPtr<WKTargetedPreviewContainer>)_createPreviewContainerWithLayerName:(NSString *)layerName |
| 10314 | { |
| 10315 | auto container = adoptNS([[WKTargetedPreviewContainer alloc] initWithContentView:self]); |
| 10316 | [container layer].anchorPoint = CGPointZero; |
| 10317 | [container layer].name = layerName; |
| 10318 | return container; |
| 10319 | } |
| 10320 | |
| 10321 | - (UIView *)containerForDropPreviews |
| 10322 | { |
| 10323 | if (!_dropPreviewContainerView) { |
| 10324 | _dropPreviewContainerView = [self _createPreviewContainerWithLayerName:@"Drop Preview Container"]; |
| 10325 | [_interactionViewsContainerView addSubview:_dropPreviewContainerView.get()]; |
| 10326 | } |
| 10327 | |
| 10328 | ASSERT([_dropPreviewContainerView superview])((void)0); |
| 10329 | return _dropPreviewContainerView.get(); |
| 10330 | } |
| 10331 | |
| 10332 | - (void)_removeContainerForDropPreviews |
| 10333 | { |
| 10334 | if (!_dropPreviewContainerView) |
| 10335 | return; |
| 10336 | |
| 10337 | [std::exchange(_dropPreviewContainerView, nilnullptr) removeFromSuperview]; |
| 10338 | } |
| 10339 | |
| 10340 | - (UIView *)containerForDragPreviews |
| 10341 | { |
| 10342 | if (!_dragPreviewContainerView) { |
| 10343 | _dragPreviewContainerView = [self _createPreviewContainerWithLayerName:@"Drag Preview Container"]; |
| 10344 | [_interactionViewsContainerView addSubview:_dragPreviewContainerView.get()]; |
| 10345 | } |
| 10346 | |
| 10347 | ASSERT([_dragPreviewContainerView superview])((void)0); |
| 10348 | return _dragPreviewContainerView.get(); |
| 10349 | } |
| 10350 | |
| 10351 | - (void)_removeContainerForDragPreviews |
| 10352 | { |
| 10353 | if (!_dragPreviewContainerView) |
| 10354 | return; |
| 10355 | |
| 10356 | [std::exchange(_dragPreviewContainerView, nilnullptr) removeFromSuperview]; |
| 10357 | } |
| 10358 | |
| 10359 | - (UIView *)containerForContextMenuHintPreviews |
| 10360 | { |
| 10361 | if (!_contextMenuHintContainerView) { |
| 10362 | _contextMenuHintContainerView = [self _createPreviewContainerWithLayerName:@"Context Menu Hint Preview Container"]; |
| 10363 | RELEASE_LOG(ViewState, "%p - [pageProxyID=%" PRIu64 "] Created container for context menu hint previews: %p"clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG228" ) = "%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop |
| 10364 | , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView.get())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG228" ) = "%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "%p - [pageProxyID=%" "ll" "u" "] Created container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10365 | |
| 10366 | RetainPtr<UIView> containerView; |
| 10367 | |
| 10368 | if (auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate)) { |
| 10369 | if ([uiDelegate respondsToSelector:@selector(_contextMenuHintPreviewContainerViewForWebView:)]) |
| 10370 | containerView = [uiDelegate _contextMenuHintPreviewContainerViewForWebView:self.webView]; |
| 10371 | } |
| 10372 | |
| 10373 | if (!containerView) |
| 10374 | containerView = _interactionViewsContainerView; |
| 10375 | |
| 10376 | [containerView addSubview:_contextMenuHintContainerView.get()]; |
| 10377 | } |
| 10378 | |
| 10379 | ASSERT([_contextMenuHintContainerView superview])((void)0); |
| 10380 | return _contextMenuHintContainerView.get(); |
| 10381 | } |
| 10382 | |
| 10383 | - (void)_removeContainerForContextMenuHintPreviews |
| 10384 | { |
| 10385 | if (!_contextMenuHintContainerView) |
| 10386 | return; |
| 10387 | |
| 10388 | RELEASE_LOG(ViewState, "%p - [pageProxyID=%" PRIu64 "] Removing container for context menu hint previews: %p"clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG229" ) = "%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop |
| 10389 | , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView.get())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG229" ) = "%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "%p - [pageProxyID=%" "ll" "u" "] Removing container for context menu hint previews: %p" , self, _page ? _page->identifier().toUInt64() : 0, _contextMenuHintContainerView .get()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10390 | [std::exchange(_contextMenuHintContainerView, nilnullptr) removeFromSuperview]; |
| 10391 | |
| 10392 | _scrollViewForTargetedPreview = nilnullptr; |
| 10393 | _scrollViewForTargetedPreviewInitialOffset = CGPointZero; |
| 10394 | } |
| 10395 | |
| 10396 | - (void)_updateFrameOfContainerForContextMenuHintPreviewsIfNeeded |
| 10397 | { |
| 10398 | if (!_contextMenuHintContainerView) |
| 10399 | return; |
| 10400 | |
| 10401 | CGPoint newOffset = [protect(_scrollViewForTargetedPreview) convertPoint:CGPointZero toView:[protect(_contextMenuHintContainerView.get()) superview]]; |
| 10402 | |
| 10403 | CGRect frame = [_contextMenuHintContainerView frame]; |
| 10404 | frame.origin.x = newOffset.x - _scrollViewForTargetedPreviewInitialOffset.x; |
| 10405 | frame.origin.y = newOffset.y - _scrollViewForTargetedPreviewInitialOffset.y; |
| 10406 | [_contextMenuHintContainerView setFrame:frame]; |
| 10407 | } |
| 10408 | |
| 10409 | - (void)_updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:(std::optional<WebCore::ScrollingNodeID>)scrollingNodeID |
| 10410 | { |
| 10411 | if (scrollingNodeID) { |
| 10412 | if (RetainPtr scrollViewForScrollingNode = [self _scrollViewForScrollingNodeID:scrollingNodeID]) |
| 10413 | _scrollViewForTargetedPreview = scrollViewForScrollingNode.get(); |
| 10414 | } |
| 10415 | |
| 10416 | if (!_scrollViewForTargetedPreview) |
| 10417 | _scrollViewForTargetedPreview = self.webView.scrollView; |
| 10418 | |
| 10419 | _scrollViewForTargetedPreviewInitialOffset = [protect(_scrollViewForTargetedPreview) convertPoint:CGPointZero toView:[protect(_contextMenuHintContainerView.get()) superview]]; |
| 10420 | } |
| 10421 | |
| 10422 | #pragma mark - WKDeferringGestureRecognizerDelegate |
| 10423 | |
| 10424 | - (WebKit::ShouldDeferGestures)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer willBeginTouchesWithEvent:(UIEvent *)event |
| 10425 | { |
| 10426 | protect(self.gestureRecognizerConsistencyEnforcer)->beginTracking(deferringGestureRecognizer); |
| 10427 | |
| 10428 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 10429 | if ([_imageAnalysisInteraction interactableItemExistsAtPoint:[deferringGestureRecognizer locationInView:self]]) |
| 10430 | return WebKit::ShouldDeferGestures::No; |
| 10431 | #endif |
| 10432 | |
| 10433 | return [self gestureRecognizer:deferringGestureRecognizer isInterruptingMomentumScrollingWithEvent:event] ? WebKit::ShouldDeferGestures::No : WebKit::ShouldDeferGestures::Yes; |
| 10434 | } |
| 10435 | |
| 10436 | - (void)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didTransitionToState:(UIGestureRecognizerState)state |
| 10437 | { |
| 10438 | if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateFailed || state == UIGestureRecognizerStateCancelled) |
| 10439 | protect(self.gestureRecognizerConsistencyEnforcer)->endTracking(deferringGestureRecognizer); |
| 10440 | } |
| 10441 | |
| 10442 | - (void)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didEndTouchesWithEvent:(UIEvent *)event |
| 10443 | { |
| 10444 | protect(protect(self).get().gestureRecognizerConsistencyEnforcer).get().endTracking(deferringGestureRecognizer); |
| 10445 | |
| 10446 | if (deferringGestureRecognizer.state != UIGestureRecognizerStatePossible) |
| 10447 | return; |
| 10448 | |
| 10449 | if (_page->isHandlingPreventableTouchStart() && [self _isTouchStartDeferringGesture:deferringGestureRecognizer]) |
| 10450 | return; |
| 10451 | |
| 10452 | if (_page->isHandlingPreventableTouchMove() && deferringGestureRecognizer == _touchMoveDeferringGestureRecognizer) |
| 10453 | return; |
| 10454 | |
| 10455 | if (_page->isHandlingPreventableTouchEnd() && [self _isTouchEndDeferringGesture:deferringGestureRecognizer]) |
| 10456 | return; |
| 10457 | |
| 10458 | if ([_touchEventGestureRecognizer state] == UIGestureRecognizerStatePossible) |
| 10459 | return; |
| 10460 | |
| 10461 | // In the case where the touch event gesture recognizer has failed or ended already and we are not in the middle of handling |
| 10462 | // an asynchronous (but preventable) touch event, this is our last chance to lift the gesture "gate" by failing the deferring |
| 10463 | // gesture recognizer. |
| 10464 | deferringGestureRecognizer.state = UIGestureRecognizerStateFailed; |
| 10465 | } |
| 10466 | |
| 10467 | - (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferOtherGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer |
| 10468 | { |
| 10469 | #if ENABLE(IOS_TOUCH_EVENTS)(defined 0 && 0) |
| 10470 | if ([self _touchEventsMustRequireGestureRecognizerToFail:gestureRecognizer]) |
| 10471 | return NO__objc_no; |
| 10472 | |
| 10473 | if (_failedTouchStartDeferringGestures && _failedTouchStartDeferringGestures->contains(deferringGestureRecognizer) |
| 10474 | && deferringGestureRecognizer.state == UIGestureRecognizerStatePossible) { |
| 10475 | // This deferring gesture no longer has an oppportunity to defer native gestures (either because the touch region did not have any |
| 10476 | // active touch event listeners, or because any active touch event listeners on the page have already executed, and did not prevent |
| 10477 | // default). UIKit may have already reset the gesture to Possible state underneath us, in which case we still need to treat it as |
| 10478 | // if it has already failed; otherwise, we will incorrectly defer other gestures in the web view, such as scroll view pinching. |
| 10479 | return NO__objc_no; |
| 10480 | } |
| 10481 | |
| 10482 | if (gestureRecognizer == _keyboardDismissalGestureRecognizer) |
| 10483 | return NO__objc_no; |
| 10484 | |
| 10485 | auto webView = _webView.getAutoreleased(); |
| 10486 | auto view = gestureRecognizer.view; |
| 10487 | BOOL gestureIsInstalledOnOrUnderWebView = NO__objc_no; |
| 10488 | while (view) { |
| 10489 | if (view == webView) { |
| 10490 | gestureIsInstalledOnOrUnderWebView = YES__objc_yes; |
| 10491 | break; |
| 10492 | } |
| 10493 | view = view.superview; |
| 10494 | } |
| 10495 | |
| 10496 | if (!gestureIsInstalledOnOrUnderWebView && ![self _gestureRecognizerCanBePreventedByTouchEvents:gestureRecognizer]) |
| 10497 | return NO__objc_no; |
| 10498 | |
| 10499 | if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class]) |
| 10500 | return NO__objc_no; |
| 10501 | |
| 10502 | if (gestureRecognizer == _touchEventGestureRecognizer) |
| 10503 | return NO__objc_no; |
| 10504 | |
| 10505 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 10506 | if ([_mouseInteraction hasGesture:gestureRecognizer]) |
| 10507 | return NO__objc_no; |
| 10508 | #endif |
| 10509 | |
| 10510 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 10511 | if (deferringGestureRecognizer == _imageAnalysisDeferringGestureRecognizer) |
| 10512 | return [self shouldDeferGestureDueToImageAnalysis:gestureRecognizer]; |
| 10513 | #endif |
| 10514 | |
| 10515 | if (deferringGestureRecognizer == _touchMoveDeferringGestureRecognizer) |
| 10516 | return [gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class] || [gestureRecognizer isKindOfClass:UIPinchGestureRecognizer.class]; |
| 10517 | |
| 10518 | BOOL mayDelayReset = [&]() -> BOOL { |
| 10519 | if ([self _isContextMenuGestureRecognizerForFailureRelationships:gestureRecognizer]) |
| 10520 | return YES__objc_yes; |
| 10521 | |
| 10522 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 10523 | if ([self _isDragInitiationGestureRecognizer:gestureRecognizer]) |
| 10524 | return YES__objc_yes; |
| 10525 | #endif |
| 10526 | |
| 10527 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 10528 | if (gestureRecognizer == _imageAnalysisGestureRecognizer) |
| 10529 | return YES__objc_yes; |
| 10530 | #endif |
| 10531 | |
| 10532 | if (gestureRecognizer._wk_isTapAndAHalf) |
| 10533 | return YES__objc_yes; |
| 10534 | |
| 10535 | if (gestureRecognizer._wk_isTextInteractionLoupeGesture) |
| 10536 | return YES__objc_yes; |
| 10537 | |
| 10538 | if (gestureRecognizer == _highlightLongPressGestureRecognizer) |
| 10539 | return YES__objc_yes; |
| 10540 | |
| 10541 | if (auto *tapGesture = dynamic_objc_cast<UITapGestureRecognizer>(gestureRecognizer)) |
| 10542 | return tapGesture.numberOfTapsRequired > 1 && tapGesture.numberOfTouchesRequired < 2; |
| 10543 | |
| 10544 | return NO__objc_no; |
| 10545 | }(); |
| 10546 | |
| 10547 | BOOL isSyntheticTap = [gestureRecognizer isKindOfClass:WKSyntheticTapGestureRecognizer.class]; |
| 10548 | if ([gestureRecognizer isKindOfClass:UITapGestureRecognizer.class]) { |
| 10549 | if (deferringGestureRecognizer == _touchEndDeferringGestureRecognizerForSyntheticTapGestures) |
| 10550 | return isSyntheticTap; |
| 10551 | |
| 10552 | if (deferringGestureRecognizer == _touchEndDeferringGestureRecognizerForDelayedResettableGestures) |
| 10553 | return !isSyntheticTap && mayDelayReset; |
| 10554 | |
| 10555 | if (deferringGestureRecognizer == _touchEndDeferringGestureRecognizerForImmediatelyResettableGestures) |
| 10556 | return !isSyntheticTap && !mayDelayReset; |
| 10557 | } |
| 10558 | |
| 10559 | if (isSyntheticTap) |
| 10560 | return deferringGestureRecognizer == _touchStartDeferringGestureRecognizerForSyntheticTapGestures; |
| 10561 | |
| 10562 | if (mayDelayReset) |
| 10563 | return deferringGestureRecognizer == _touchStartDeferringGestureRecognizerForDelayedResettableGestures; |
| 10564 | |
| 10565 | return deferringGestureRecognizer == _touchStartDeferringGestureRecognizerForImmediatelyResettableGestures; |
| 10566 | #else |
| 10567 | UNUSED_PARAM(deferringGestureRecognizer)(void)deferringGestureRecognizer; |
| 10568 | UNUSED_PARAM(gestureRecognizer)(void)gestureRecognizer; |
| 10569 | return NO__objc_no; |
| 10570 | #endif |
| 10571 | } |
| 10572 | |
| 10573 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 10574 | |
| 10575 | static BOOL shouldEnableDragInteractionForPolicy(_WKDragInteractionPolicy policy) |
| 10576 | { |
| 10577 | switch (policy) { |
| 10578 | case _WKDragInteractionPolicyAlwaysEnable: |
| 10579 | return YES__objc_yes; |
| 10580 | case _WKDragInteractionPolicyAlwaysDisable: |
| 10581 | return NO__objc_no; |
| 10582 | default: |
| 10583 | return [UIDragInteraction isEnabledByDefault]; |
| 10584 | } |
| 10585 | } |
| 10586 | |
| 10587 | - (void)_didChangeDragInteractionPolicy |
| 10588 | { |
| 10589 | [_dragInteraction setEnabled:shouldEnableDragInteractionForPolicy(self.webView._dragInteractionPolicy)]; |
| 10590 | } |
| 10591 | |
| 10592 | - (NSTimeInterval)dragLiftDelay |
| 10593 | { |
| 10594 | static const NSTimeInterval mediumDragLiftDelay = 0.5; |
| 10595 | static const NSTimeInterval longDragLiftDelay = 0.65; |
| 10596 | auto dragLiftDelay = self.webView.configuration._dragLiftDelay; |
| 10597 | if (dragLiftDelay == _WKDragLiftDelayMedium) |
| 10598 | return mediumDragLiftDelay; |
| 10599 | if (dragLiftDelay == _WKDragLiftDelayLong) |
| 10600 | return longDragLiftDelay; |
| 10601 | return _UIDragInteractionDefaultLiftDelay(); |
| 10602 | } |
| 10603 | |
| 10604 | - (id <WKUIDelegatePrivate>)webViewUIDelegate |
| 10605 | { |
| 10606 | return (id <WKUIDelegatePrivate>)[_webView.get() UIDelegate]; |
| 10607 | } |
| 10608 | |
| 10609 | - (Class)_dragInteractionClass |
| 10610 | { |
| 10611 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 10612 | if (self.shouldUseAsyncInteractions) |
| 10613 | return [BEDragInteraction class]; |
| 10614 | #endif |
| 10615 | return UIDragInteraction.class; |
| 10616 | } |
| 10617 | |
| 10618 | - (void)setUpDragAndDropInteractions |
| 10619 | { |
| 10620 | _dragInteraction = adoptNS([[self._dragInteractionClass alloc] initWithDelegate:self]); |
| 10621 | _dropInteraction = adoptNS([[UIDropInteraction alloc] initWithDelegate:self]); |
| 10622 | [_dragInteraction setEnabled:shouldEnableDragInteractionForPolicy(self.webView._dragInteractionPolicy)]; |
| 10623 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 10624 | if (!self.shouldUseAsyncInteractions) { |
| 10625 | [_dragInteraction _setLiftDelay:self.dragLiftDelay]; |
| 10626 | [_dragInteraction _setAllowsPointerDragBeforeLiftDelay:NO__objc_no]; |
| 10627 | } |
| 10628 | #endif |
| 10629 | |
| 10630 | [self addInteraction:_dragInteraction.get()]; |
| 10631 | [self addInteraction:_dropInteraction.get()]; |
| 10632 | } |
| 10633 | |
| 10634 | - (void)teardownDragAndDropInteractions |
| 10635 | { |
| 10636 | if (_dragInteraction) |
| 10637 | [self removeInteraction:_dragInteraction.get()]; |
| 10638 | |
| 10639 | if (_dropInteraction) |
| 10640 | [self removeInteraction:_dropInteraction.get()]; |
| 10641 | |
| 10642 | _dragInteraction = nilnullptr; |
| 10643 | _dropInteraction = nilnullptr; |
| 10644 | |
| 10645 | [self cleanUpDragSourceSessionState]; |
| 10646 | } |
| 10647 | |
| 10648 | - (void)_startDrag:(RetainPtr<CGImageRef>)image item:(const WebCore::DragItem&)item nodeID:(std::optional<WebCore::NodeIdentifier>)nodeID |
| 10649 | { |
| 10650 | ASSERT(item.sourceAction)((void)0); |
| 10651 | |
| 10652 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 10653 | _dragDropInteractionState.setElementIdentifier(nodeID); |
| 10654 | if (item.modelLayerID && _page) { |
| 10655 | if (RefPtr modelPresentationManager = _page->modelPresentationManagerProxy()) { |
| 10656 | if (RetainPtr viewForDragPreview = modelPresentationManager->startDragForModel(*item.modelLayerID)) { |
| 10657 | _dragDropInteractionState.stageDragItem(item, viewForDragPreview); |
| 10658 | return; |
| 10659 | } |
| 10660 | } |
| 10661 | } |
| 10662 | #endif |
| 10663 | |
| 10664 | if (item.promisedAttachmentInfo) |
| 10665 | [self _prepareToDragPromisedAttachment:item.promisedAttachmentInfo]; |
| 10666 | |
| 10667 | auto dragImage = adoptNS([[UIImage alloc] initWithCGImage:image.get() scale:protect(_page)->deviceScaleFactor() orientation:UIImageOrientationUp]); |
| 10668 | _dragDropInteractionState.stageDragItem(item, dragImage.get()); |
| 10669 | } |
| 10670 | |
| 10671 | - (void)_didHandleAdditionalDragItemsRequest:(BOOL)added |
| 10672 | { |
| 10673 | auto completion = _dragDropInteractionState.takeAddDragItemCompletionBlock(); |
| 10674 | if (!completion) |
| 10675 | return; |
| 10676 | |
| 10677 | auto *registrationLists = [[WebItemProviderPasteboard sharedInstance] takeRegistrationLists]; |
| 10678 | if (!added || ![registrationLists count] || !_dragDropInteractionState.hasStagedDragSource()) { |
| 10679 | _dragDropInteractionState.clearStagedDragSource(); |
| 10680 | completion(@[ ]); |
| 10681 | return; |
| 10682 | } |
| 10683 | |
| 10684 | auto stagedDragSource = _dragDropInteractionState.stagedDragSource(); |
| 10685 | NSArray *dragItemsToAdd = [self _itemsForBeginningOrAddingToSessionWithRegistrationLists:registrationLists stagedDragSource:stagedDragSource]; |
| 10686 | |
| 10687 | RELEASE_LOG(DragAndDrop, "Drag session: %p adding %tu items", _dragDropInteractionState.dragSession(), dragItemsToAdd.count)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session: %p adding %tu items"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG230" ) = "Drag session: %p adding %tu items"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session: %p adding %tu items", _dragDropInteractionState .dragSession(), dragItemsToAdd.count)]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Drag session: %p adding %tu items", _dragDropInteractionState .dragSession(), dragItemsToAdd.count), (uint32_t)sizeof(_os_fmt_buf )) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10688 | _dragDropInteractionState.clearStagedDragSource(dragItemsToAdd.count ? WebKit::DragDropInteractionState::DidBecomeActive::Yes : WebKit::DragDropInteractionState::DidBecomeActive::No); |
| 10689 | |
| 10690 | completion(dragItemsToAdd); |
| 10691 | |
| 10692 | if (dragItemsToAdd.count) |
| 10693 | protect(_page)->didStartDrag(); |
| 10694 | } |
| 10695 | |
| 10696 | - (void)_didHandleDragStartRequest:(BOOL)started |
| 10697 | { |
| 10698 | BlockPtr<void()> savedCompletionBlock = _dragDropInteractionState.takeDragStartCompletionBlock(); |
| 10699 | ASSERT(savedCompletionBlock)((void)0); |
| 10700 | |
| 10701 | RELEASE_LOG(DragAndDrop, "Handling drag start request (started: %d, completion block: %p)", started, savedCompletionBlock.get())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Handling drag start request (started: %d, completion block: %p)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG231" ) = "Handling drag start request (started: %d, completion block: %p)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Handling drag start request (started: %d, completion block: %p)" , started, savedCompletionBlock.get())]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Handling drag start request (started: %d, completion block: %p)" , started, savedCompletionBlock.get()), (uint32_t)sizeof(_os_fmt_buf )) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10702 | if (savedCompletionBlock) |
| 10703 | savedCompletionBlock(); |
| 10704 | |
| 10705 | if (!protect(_dragDropInteractionState.dragSession()).get().items.count) { |
| 10706 | auto positionForDragEnd = WebCore::roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd()); |
| 10707 | [self cleanUpDragSourceSessionState]; |
| 10708 | if (started) { |
| 10709 | // A client of the Objective C SPI or UIKit might have prevented the drag from beginning entirely in the UI process, in which case |
| 10710 | // we need to balance the `dragstart` event with a `dragend`. |
| 10711 | protect(_page)->dragEnded(positionForDragEnd, positionForDragEnd, { }); |
| 10712 | } |
| 10713 | } |
| 10714 | } |
| 10715 | |
| 10716 | - (void)computeClientAndGlobalPointsForDropSession:(id <UIDropSession>)session outClientPoint:(CGPoint *)outClientPoint outGlobalPoint:(CGPoint *)outGlobalPoint |
| 10717 | { |
| 10718 | // FIXME: This makes the behavior of drag events on iOS consistent with other synthetic mouse events on iOS (see WebPage::completeSyntheticClick). |
| 10719 | // However, we should experiment with making the client position relative to the window and the global position in document coordinates. See |
| 10720 | // https://bugs.webkit.org/show_bug.cgi?id=173855 for more details. |
| 10721 | auto locationInContentView = [session locationInView:self]; |
| 10722 | if (outClientPoint) |
| 10723 | *outClientPoint = locationInContentView; |
| 10724 | |
| 10725 | if (outGlobalPoint) |
| 10726 | *outGlobalPoint = locationInContentView; |
| 10727 | } |
| 10728 | |
| 10729 | static UIDropOperation dropOperationForWebCoreDragOperation(std::optional<WebCore::DragOperation> operation) |
| 10730 | { |
| 10731 | if (operation) { |
| 10732 | if (*operation == WebCore::DragOperation::Move) |
| 10733 | return UIDropOperationMove; |
| 10734 | if (*operation == WebCore::DragOperation::Copy) |
| 10735 | return UIDropOperationCopy; |
| 10736 | } |
| 10737 | return UIDropOperationCancel; |
| 10738 | } |
| 10739 | |
| 10740 | static std::optional<WebCore::DragOperation> coreDragOperationForUIDropOperation(UIDropOperation dropOperation) |
| 10741 | { |
| 10742 | switch (dropOperation) { |
| 10743 | case UIDropOperationCancel: |
| 10744 | return std::nullopt; |
| 10745 | case UIDropOperationForbidden: |
| 10746 | return WebCore::DragOperation::Private; |
| 10747 | case UIDropOperationCopy: |
| 10748 | return WebCore::DragOperation::Copy; |
| 10749 | case UIDropOperationMove: |
| 10750 | return WebCore::DragOperation::Move; |
| 10751 | } |
| 10752 | ASSERT_NOT_REACHED()((void)0); |
| 10753 | return std::nullopt; |
| 10754 | } |
| 10755 | |
| 10756 | - (WebCore::DragData)dragDataForDropSession:(id <UIDropSession>)session dragDestinationAction:(WKDragDestinationAction)dragDestinationAction |
| 10757 | { |
| 10758 | CGPoint global; |
| 10759 | CGPoint client; |
| 10760 | [self computeClientAndGlobalPointsForDropSession:session outClientPoint:&client outGlobalPoint:&global]; |
| 10761 | |
| 10762 | auto dragOperationMask = WebCore::anyDragOperation(); |
| 10763 | if (!session.allowsMoveOperation) |
| 10764 | dragOperationMask.remove(WebCore::DragOperation::Move); |
| 10765 | |
| 10766 | return { |
| 10767 | session, |
| 10768 | WebCore::roundedIntPoint(client), |
| 10769 | WebCore::roundedIntPoint(global), |
| 10770 | dragOperationMask, |
| 10771 | { }, |
| 10772 | WebKit::coreDragDestinationActionMask(dragDestinationAction), |
| 10773 | _page->webPageIDInMainFrameProcess() |
| 10774 | }; |
| 10775 | } |
| 10776 | |
| 10777 | - (void)cleanUpDragSourceSessionState |
| 10778 | { |
| 10779 | if (_waitingForEditDragSnapshot) |
| 10780 | return; |
| 10781 | |
| 10782 | if (_dragDropInteractionState.dragSession() || _dragDropInteractionState.isPerformingDrop()) |
| 10783 | RELEASE_LOG(DragAndDrop, "Cleaning up dragging state (has pending operation: %d)", [[WebItemProviderPasteboard sharedInstance] hasPendingOperation])clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Cleaning up dragging state (has pending operation: %d)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG232" ) = "Cleaning up dragging state (has pending operation: %d)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Cleaning up dragging state (has pending operation: %d)" , [[WebItemProviderPasteboard sharedInstance] hasPendingOperation ])]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Cleaning up dragging state (has pending operation: %d)" , [[WebItemProviderPasteboard sharedInstance] hasPendingOperation ]), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10784 | |
| 10785 | if (![[WebItemProviderPasteboard sharedInstance] hasPendingOperation]) { |
| 10786 | // If we're performing a drag operation, don't clear out the pasteboard yet, since another web view may still require access to it. |
| 10787 | // The pasteboard will be cleared after the last client is finished performing a drag operation using the item providers. |
| 10788 | [[WebItemProviderPasteboard sharedInstance] setItemProviders:nilnullptr]; |
| 10789 | } |
| 10790 | |
| 10791 | [[WebItemProviderPasteboard sharedInstance] clearRegistrationLists]; |
| 10792 | [self _restoreEditMenuIfNeeded]; |
| 10793 | |
| 10794 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 10795 | if (_page) { |
| 10796 | if (RefPtr modelPresentationManager = _page->modelPresentationManagerProxy()) |
| 10797 | modelPresentationManager->doneWithCurrentDragSession(); |
| 10798 | |
| 10799 | if (_dragDropInteractionState.nodeIdentifier()) |
| 10800 | _page->modelDragEnded(_dragDropInteractionState.nodeIdentifier().value()); |
| 10801 | } |
| 10802 | #endif |
| 10803 | |
| 10804 | [self _removeContainerForDragPreviews]; |
| 10805 | [std::exchange(_visibleContentViewSnapshot, nilnullptr) removeFromSuperview]; |
| 10806 | [self _removeDropCaret]; |
| 10807 | _shouldRestoreEditMenuAfterDrop = NO__objc_no; |
| 10808 | |
| 10809 | _dragDropInteractionState.dragAndDropSessionsDidBecomeInactive(); |
| 10810 | _dragDropInteractionState = { }; |
| 10811 | } |
| 10812 | |
| 10813 | - (void)_insertDropCaret:(CGRect)rect |
| 10814 | { |
| 10815 | #if HAVE(UI_TEXT_CURSOR_DROP_POSITION_ANIMATOR)(defined 1 && 1) |
| 10816 | if (self._shouldUseTextCursorDragAnimator) { |
| 10817 | _editDropTextCursorView = [adoptNS([[UITextSelectionDisplayInteraction alloc] initWithTextInput:self delegate:self]) cursorView]; |
| 10818 | [self addSubview:_editDropTextCursorView.get()]; |
| 10819 | [_editDropTextCursorView setFrame:rect]; |
| 10820 | _editDropCaretAnimator = adoptNS([[UITextCursorDropPositionAnimator alloc] initWithTextCursorView:_editDropTextCursorView.get() textInput:self]); |
| 10821 | [_editDropCaretAnimator setCursorVisible:YES__objc_yes animated:YES__objc_yes]; |
| 10822 | [_editDropCaretAnimator placeCursorAtPosition:[WKTextPosition textPositionWithRect:rect] animated:NO__objc_no]; |
| 10823 | return; |
| 10824 | } |
| 10825 | #endif // HAVE(UI_TEXT_CURSOR_DROP_POSITION_ANIMATOR) |
| 10826 | _editDropCaretView = adoptNS([[_UITextDragCaretView alloc] initWithTextInputView:self]); |
| 10827 | [_editDropCaretView insertAtPosition:[WKTextPosition textPositionWithRect:rect]]; |
| 10828 | } |
| 10829 | |
| 10830 | - (void)_removeDropCaret |
| 10831 | { |
| 10832 | [std::exchange(_editDropCaretView, nilnullptr) remove]; |
| 10833 | #if HAVE(UI_TEXT_CURSOR_DROP_POSITION_ANIMATOR)(defined 1 && 1) |
| 10834 | [std::exchange(_editDropCaretAnimator, nilnullptr) setCursorVisible:NO__objc_no animated:NO__objc_no]; |
| 10835 | [std::exchange(_editDropTextCursorView, nilnullptr) removeFromSuperview]; |
| 10836 | #endif |
| 10837 | } |
| 10838 | |
| 10839 | static NSArray<NSItemProvider *> *extractItemProvidersFromDragItems(NSArray<UIDragItem *> *dragItems) |
| 10840 | { |
| 10841 | NSMutableArray<NSItemProvider *> *providers = [NSMutableArray array]; |
| 10842 | for (UIDragItem *item in dragItems) { |
| 10843 | if (NSItemProvider *provider = item.itemProvider) |
| 10844 | [providers addObject:provider]; |
| 10845 | } |
| 10846 | return providers; |
| 10847 | } |
| 10848 | |
| 10849 | static NSArray<NSItemProvider *> *extractItemProvidersFromDropSession(id <UIDropSession> session) |
| 10850 | { |
| 10851 | return extractItemProvidersFromDragItems(session.items); |
| 10852 | } |
| 10853 | |
| 10854 | - (void)_willReceiveEditDragSnapshot |
| 10855 | { |
| 10856 | _waitingForEditDragSnapshot = YES__objc_yes; |
| 10857 | } |
| 10858 | |
| 10859 | - (void)_didReceiveEditDragSnapshot:(RefPtr<WebCore::TextIndicator>&&)textIndicator |
| 10860 | { |
| 10861 | _waitingForEditDragSnapshot = NO__objc_no; |
| 10862 | |
| 10863 | [self _deliverDelayedDropPreviewIfPossible:WTF::move(textIndicator)]; |
| 10864 | [self cleanUpDragSourceSessionState]; |
| 10865 | |
| 10866 | if (auto action = WTF::move(_actionToPerformAfterReceivingEditDragSnapshot)) |
| 10867 | action(); |
| 10868 | } |
| 10869 | |
| 10870 | - (void)_deliverDelayedDropPreviewIfPossible:(RefPtr<WebCore::TextIndicator>&&)textIndicator |
| 10871 | { |
| 10872 | if (!_visibleContentViewSnapshot) |
| 10873 | return; |
| 10874 | |
| 10875 | if (!textIndicator) |
| 10876 | return; |
| 10877 | |
| 10878 | if (!textIndicator->contentImage()) |
| 10879 | return; |
| 10880 | |
| 10881 | RefPtr snapshotWithoutSelection = textIndicator->contentImageWithoutSelection(); |
| 10882 | if (!snapshotWithoutSelection) |
| 10883 | return; |
| 10884 | |
| 10885 | auto unselectedSnapshotImage = snapshotWithoutSelection->nativeImage(); |
| 10886 | if (!unselectedSnapshotImage) |
| 10887 | return; |
| 10888 | |
| 10889 | if (!_dropAnimationCount) |
| 10890 | return; |
| 10891 | |
| 10892 | auto unselectedContentImageForEditDrag = adoptNS([[UIImage alloc] initWithCGImage:unselectedSnapshotImage->platformImage().get() scale:protect(_page)->deviceScaleFactor() orientation:UIImageOrientationUp]); |
| 10893 | _unselectedContentSnapshot = adoptNS([[UIImageView alloc] initWithImage:unselectedContentImageForEditDrag.get()]); |
| 10894 | [_unselectedContentSnapshot setFrame:textIndicator->contentImageWithoutSelectionRectInRootViewCoordinates()]; |
| 10895 | |
| 10896 | [self insertSubview:_unselectedContentSnapshot.get() belowSubview:_visibleContentViewSnapshot.get()]; |
| 10897 | _dragDropInteractionState.deliverDelayedDropPreview(self, self.containerForDropPreviews, WTF::move(textIndicator)); |
| 10898 | } |
| 10899 | |
| 10900 | - (void)_didPerformDragOperation:(BOOL)handled |
| 10901 | { |
| 10902 | RELEASE_LOG(DragAndDrop, "Finished performing drag controller operation (handled: %d)", handled)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Finished performing drag controller operation (handled: %d)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG233" ) = "Finished performing drag controller operation (handled: %d)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Finished performing drag controller operation (handled: %d)" , handled)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Finished performing drag controller operation (handled: %d)" , handled), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10903 | [[WebItemProviderPasteboard sharedInstance] decrementPendingOperationCount]; |
| 10904 | RetainPtr dropSession = _dragDropInteractionState.dropSession(); |
| 10905 | if ([self.webViewUIDelegate respondsToSelector:@selector(_webView:dataInteractionOperationWasHandled:forSession:itemProviders:)]) |
| 10906 | [self.webViewUIDelegate _webView:self.webView dataInteractionOperationWasHandled:handled forSession:dropSession.get() itemProviders:[WebItemProviderPasteboard sharedInstance].itemProviders]; |
| 10907 | |
| 10908 | CGPoint global; |
| 10909 | CGPoint client; |
| 10910 | [self computeClientAndGlobalPointsForDropSession:dropSession.get() outClientPoint:&client outGlobalPoint:&global]; |
| 10911 | [self cleanUpDragSourceSessionState]; |
| 10912 | auto currentDragOperation = _page->currentDragOperation(); |
| 10913 | protect(_page)->dragEnded(WebCore::roundedIntPoint(client), WebCore::roundedIntPoint(global), currentDragOperation ? *currentDragOperation : OptionSet<WebCore::DragOperation>({ })); |
| 10914 | } |
| 10915 | |
| 10916 | - (void)_didChangeDragCaretRect:(CGRect)previousRect currentRect:(CGRect)rect |
| 10917 | { |
| 10918 | BOOL previousRectIsEmpty = CGRectIsEmpty(previousRect); |
| 10919 | BOOL currentRectIsEmpty = CGRectIsEmpty(rect); |
| 10920 | if (previousRectIsEmpty && currentRectIsEmpty) |
| 10921 | return; |
| 10922 | |
| 10923 | if (previousRectIsEmpty) { |
| 10924 | [self _insertDropCaret:rect]; |
| 10925 | return; |
| 10926 | } |
| 10927 | |
| 10928 | if (currentRectIsEmpty) { |
| 10929 | [self _removeDropCaret]; |
| 10930 | return; |
| 10931 | } |
| 10932 | |
| 10933 | RetainPtr caretPosition = [WKTextPosition textPositionWithRect:rect]; |
| 10934 | #if HAVE(UI_TEXT_CURSOR_DROP_POSITION_ANIMATOR)(defined 1 && 1) |
| 10935 | [_editDropCaretAnimator placeCursorAtPosition:caretPosition.get() animated:YES__objc_yes]; |
| 10936 | #endif |
| 10937 | [_editDropCaretView updateToPosition:caretPosition.get()]; |
| 10938 | } |
| 10939 | |
| 10940 | - (void)_prepareToDragPromisedAttachment:(const WebCore::PromisedAttachmentInfo&)info |
| 10941 | { |
| 10942 | auto session = retainPtr(_dragDropInteractionState.dragSession()); |
| 10943 | if (!session) { |
| 10944 | ASSERT_NOT_REACHED()((void)0); |
| 10945 | return; |
| 10946 | } |
| 10947 | |
| 10948 | RELEASE_LOG(DragAndDrop, "Drag session: %p preparing to drag with attachment identifier: %s", session.get(), info.attachmentIdentifier.utf8().data())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session: %p preparing to drag with attachment identifier: %s" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG234" ) = "Drag session: %p preparing to drag with attachment identifier: %s" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drag session: %p preparing to drag with attachment identifier: %s" , session.get(), info.attachmentIdentifier.utf8().data())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session: %p preparing to drag with attachment identifier: %s" , session.get(), info.attachmentIdentifier.utf8().data()), (uint32_t )sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10949 | |
| 10950 | RetainPtr<NSString> utiType; |
| 10951 | RetainPtr<NSString> fileName; |
| 10952 | if (auto attachment = protect(_page)->attachmentForIdentifier(info.attachmentIdentifier)) { |
| 10953 | utiType = attachment->utiType().createNSString(); |
| 10954 | fileName = attachment->fileName().createNSString(); |
| 10955 | } |
| 10956 | |
| 10957 | auto registrationList = adoptNS([[WebItemProviderRegistrationInfoList alloc] init]); |
| 10958 | [registrationList setPreferredPresentationStyle:WebPreferredPresentationStyleAttachment]; |
| 10959 | if ([fileName length]) |
| 10960 | [registrationList setSuggestedName:fileName.get()]; |
| 10961 | for (size_t index = 0; index < info.additionalTypesAndData.size(); ++index) { |
| 10962 | auto nsData = protect(info.additionalTypesAndData[index].second)->createNSData(); |
| 10963 | [registrationList addData:nsData.get() forType:info.additionalTypesAndData[index].first.createNSString().get()]; |
| 10964 | } |
| 10965 | |
| 10966 | [registrationList addPromisedType:utiType.get() fileCallback:[session = WTF::move(session), weakSelf = WeakObjCPtr<WKContentView>(self), info] (WebItemProviderFileCallback callback) { |
| 10967 | auto strongSelf = weakSelf.get(); |
| 10968 | if (!strongSelf) { |
| 10969 | callback(nilnullptr, [NSError errorWithDomain:WKErrorDomain code:WKErrorWebViewInvalidated userInfo:nilnullptr]); |
| 10970 | return; |
| 10971 | } |
| 10972 | |
| 10973 | RetainPtr temporaryBlobDirectory = FileSystem::createTemporaryDirectory(@"blobs"); |
| 10974 | RetainPtr destinationURL = [NSURL fileURLWithPath:[temporaryBlobDirectory.get() stringByAppendingPathComponent:[NSUUID UUID].UUIDString] isDirectory:NO__objc_no]; |
| 10975 | |
| 10976 | if (auto attachment = protect(strongSelf->_page)->attachmentForIdentifier(info.attachmentIdentifier); attachment && !attachment->isEmpty()) { |
| 10977 | attachment->doWithFileWrapper([&](NSFileWrapper *fileWrapper) { |
| 10978 | RELEASE_LOG(DragAndDrop, "Drag session: %p delivering promised attachment: %s at path: %@", session.get(), info.attachmentIdentifier.utf8().data(), destinationURL.get().path)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session: %p delivering promised attachment: %s at path: %@" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG235" ) = "Drag session: %p delivering promised attachment: %s at path: %@" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drag session: %p delivering promised attachment: %s at path: %@" , session.get(), info.attachmentIdentifier.utf8().data(), destinationURL .get().path)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Drag session: %p delivering promised attachment: %s at path: %@" , session.get(), info.attachmentIdentifier.utf8().data(), destinationURL .get().path), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 10979 | NSError *fileWrapperError = nilnullptr; |
| 10980 | if ([fileWrapper writeToURL:destinationURL.get() options:0 originalContentsURL:nilnullptr error:&fileWrapperError]) |
| 10981 | callback(destinationURL.get(), nilnullptr); |
| 10982 | else |
| 10983 | callback(nilnullptr, fileWrapperError); |
| 10984 | }); |
| 10985 | } else |
| 10986 | callback(nilnullptr, [NSError errorWithDomain:WKErrorDomain code:WKErrorWebViewInvalidated userInfo:nilnullptr]); |
| 10987 | |
| 10988 | [protect(ensureLocalDragSessionContext(session.get())) addTemporaryDirectory:temporaryBlobDirectory.get()]; |
| 10989 | }]; |
| 10990 | |
| 10991 | WebItemProviderPasteboard *pasteboard = [WebItemProviderPasteboard sharedInstance]; |
| 10992 | pasteboard.itemProviders = @[ [registrationList itemProvider] ]; |
| 10993 | [pasteboard stageRegistrationLists:@[ registrationList.get() ]]; |
| 10994 | } |
| 10995 | |
| 10996 | - (WKDragDestinationAction)_dragDestinationActionForDropSession:(id <UIDropSession>)session |
| 10997 | { |
| 10998 | id <WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 10999 | if ([uiDelegate respondsToSelector:@selector(_webView:dragDestinationActionMaskForDraggingInfo:)]) |
| 11000 | return [uiDelegate _webView:self.webView dragDestinationActionMaskForDraggingInfo:session]; |
| 11001 | |
| 11002 | return WKDragDestinationActionAny & ~WKDragDestinationActionLoad; |
| 11003 | } |
| 11004 | |
| 11005 | - (OptionSet<WebCore::DragSourceAction>)_allowedDragSourceActions |
| 11006 | { |
| 11007 | auto allowedActions = WebCore::anyDragSourceAction(); |
| 11008 | if (!self.isFirstResponder || !_suppressSelectionAssistantReasons.isEmpty()) { |
| 11009 | // Don't allow starting a drag on a selection when selection views are not visible. |
| 11010 | allowedActions.remove(WebCore::DragSourceAction::Selection); |
| 11011 | } |
| 11012 | return allowedActions; |
| 11013 | } |
| 11014 | |
| 11015 | - (id <UIDragDropSession>)currentDragOrDropSession |
| 11016 | { |
| 11017 | if (_dragDropInteractionState.dropSession()) |
| 11018 | return _dragDropInteractionState.dropSession(); |
| 11019 | return _dragDropInteractionState.dragSession(); |
| 11020 | } |
| 11021 | |
| 11022 | - (void)_restoreEditMenuIfNeeded |
| 11023 | { |
| 11024 | if (!_shouldRestoreEditMenuAfterDrop) |
| 11025 | return; |
| 11026 | |
| 11027 | [_textInteractionWrapper didConcludeDrop]; |
| 11028 | _shouldRestoreEditMenuAfterDrop = NO__objc_no; |
| 11029 | } |
| 11030 | |
| 11031 | - (NSArray<UIDragItem *> *)_itemsForBeginningOrAddingToSessionWithRegistrationLists:(NSArray<WebItemProviderRegistrationInfoList *> *)registrationLists stagedDragSource:(const WebKit::DragSourceState&)stagedDragSource |
| 11032 | { |
| 11033 | if (!registrationLists.count) |
| 11034 | return @[ ]; |
| 11035 | |
| 11036 | NSMutableArray *adjustedItemProviders = [NSMutableArray array]; |
| 11037 | id <WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11038 | if ([uiDelegate respondsToSelector:@selector(_webView:adjustedDataInteractionItemProvidersForItemProvider:representingObjects:additionalData:)]) { |
| 11039 | // FIXME: We should consider a new UI delegate hook that accepts a list of item providers, so we don't need to invoke this delegate method repeatedly for multiple items. |
| 11040 | for (WebItemProviderRegistrationInfoList *list in registrationLists) { |
| 11041 | NSItemProvider *defaultItemProvider = list.itemProvider; |
| 11042 | if (!defaultItemProvider) |
| 11043 | continue; |
| 11044 | |
| 11045 | auto representingObjects = adoptNS([[NSMutableArray alloc] init]); |
| 11046 | auto additionalData = adoptNS([[NSMutableDictionary alloc] init]); |
| 11047 | [list enumerateItems:[representingObjects, additionalData] (id <WebItemProviderRegistrar> item, NSUInteger) { |
| 11048 | if ([item respondsToSelector:@selector(representingObjectForClient)]) |
| 11049 | [representingObjects addObject:item.representingObjectForClient]; |
| 11050 | if ([item respondsToSelector:@selector(typeIdentifierForClient)] && [item respondsToSelector:@selector(dataForClient)]) |
| 11051 | [additionalData setObject:item.dataForClient forKey:item.typeIdentifierForClient]; |
| 11052 | }]; |
| 11053 | NSArray *adjustedItems = [uiDelegate _webView:self.webView adjustedDataInteractionItemProvidersForItemProvider:defaultItemProvider representingObjects:representingObjects.get() additionalData:additionalData.get()]; |
| 11054 | if (adjustedItems.count) |
| 11055 | [adjustedItemProviders addObjectsFromArray:adjustedItems]; |
| 11056 | } |
| 11057 | } else { |
| 11058 | for (WebItemProviderRegistrationInfoList *list in registrationLists) { |
| 11059 | if (auto *defaultItemProvider = list.itemProvider) |
| 11060 | [adjustedItemProviders addObject:defaultItemProvider]; |
| 11061 | } |
| 11062 | } |
| 11063 | |
| 11064 | NSMutableArray *dragItems = [NSMutableArray arrayWithCapacity:adjustedItemProviders.count]; |
| 11065 | for (NSItemProvider *itemProvider in adjustedItemProviders) { |
| 11066 | auto item = adoptNS([[UIDragItem alloc] initWithItemProvider:itemProvider]); |
| 11067 | [item _setPrivateLocalContext:@(stagedDragSource.itemIdentifier)]; |
| 11068 | [dragItems addObject:item.get()]; |
| 11069 | } |
| 11070 | |
| 11071 | return dragItems; |
| 11072 | } |
| 11073 | |
| 11074 | - (void)insertTextPlaceholderWithSize:(CGSize)size completionHandler:(void (^)(UITextPlaceholder *))completionHandler |
| 11075 | { |
| 11076 | protect(_page)->insertTextPlaceholder(WebCore::IntSize { size }, [weakSelf = WeakObjCPtr<WKContentView>(self), completionHandler = makeBlockPtr(completionHandler)](const std::optional<WebCore::ElementContext>& placeholder) { |
| 11077 | auto strongSelf = weakSelf.get(); |
| 11078 | if (!strongSelf || ![strongSelf webView] || !placeholder) { |
| 11079 | completionHandler(nilnullptr); |
| 11080 | return; |
| 11081 | } |
| 11082 | WebCore::ElementContext placeholderToUse { *placeholder }; |
| 11083 | placeholderToUse.boundingRect = [strongSelf convertRect:placeholderToUse.boundingRect fromView:[strongSelf webView]]; |
| 11084 | completionHandler(adoptNS([[WKTextPlaceholder alloc] initWithElementContext:placeholderToUse]).get()); |
| 11085 | }); |
| 11086 | } |
| 11087 | |
| 11088 | - (void)removeTextPlaceholder:(UITextPlaceholder *)placeholder willInsertText:(BOOL)willInsertText completionHandler:(void (^)(void))completionHandler |
| 11089 | { |
| 11090 | // FIXME: Implement support for willInsertText. See <https://bugs.webkit.org/show_bug.cgi?id=208747>. |
| 11091 | if (RetainPtr wkTextPlaceholder = dynamic_objc_cast<WKTextPlaceholder>(placeholder)) |
| 11092 | protect(_page)->removeTextPlaceholder([wkTextPlaceholder elementContext], makeBlockPtr(completionHandler)); |
| 11093 | else |
| 11094 | completionHandler(); |
| 11095 | } |
| 11096 | |
| 11097 | static Vector<WebCore::IntSize> sizesOfPlaceholderElementsToInsertWhenDroppingItems(NSArray<NSItemProvider *> *itemProviders) |
| 11098 | { |
| 11099 | Vector<WebCore::IntSize> sizes; |
| 11100 | for (NSItemProvider *item in itemProviders) { |
| 11101 | if (!WebCore::MIMETypeRegistry::isSupportedImageMIMEType(WebCore::MIMETypeFromUTI(item.web_fileUploadContentTypes.firstObject))) |
| 11102 | return { }; |
| 11103 | |
| 11104 | WebCore::IntSize presentationSize(item.preferredPresentationSize); |
| 11105 | if (presentationSize.isEmpty()) |
| 11106 | return { }; |
| 11107 | |
| 11108 | sizes.append(WTF::move(presentationSize)); |
| 11109 | } |
| 11110 | return sizes; |
| 11111 | } |
| 11112 | |
| 11113 | - (BOOL)_handleDropByInsertingImagePlaceholders:(NSArray<NSItemProvider *> *)itemProviders session:(id<UIDropSession>)session |
| 11114 | { |
| 11115 | if (!self.webView._editable) |
| 11116 | return NO__objc_no; |
| 11117 | |
| 11118 | if (_dragDropInteractionState.dragSession()) |
| 11119 | return NO__objc_no; |
| 11120 | |
| 11121 | if (session.items.count != itemProviders.count) |
| 11122 | return NO__objc_no; |
| 11123 | |
| 11124 | auto imagePlaceholderSizes = sizesOfPlaceholderElementsToInsertWhenDroppingItems(itemProviders); |
| 11125 | if (imagePlaceholderSizes.isEmpty()) |
| 11126 | return NO__objc_no; |
| 11127 | |
| 11128 | RELEASE_LOG(DragAndDrop, "Inserting dropped image placeholders for session: %p", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Inserting dropped image placeholders for session: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG236" ) = "Inserting dropped image placeholders for session: %p"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Inserting dropped image placeholders for session: %p", session )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Inserting dropped image placeholders for session: %p" , session), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11129 | |
| 11130 | protect(_page)->insertDroppedImagePlaceholders(imagePlaceholderSizes, [protectedSelf = retainPtr(self), dragItems = retainPtr(session.items)] (auto& placeholderRects, auto&& textIndicator) { |
| 11131 | auto& state = protectedSelf->_dragDropInteractionState; |
| 11132 | if (!textIndicator || !protectedSelf->_dropAnimationCount) { |
| 11133 | RELEASE_LOG(DragAndDrop, "Failed to animate image placeholders: missing text indicator data.")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Failed to animate image placeholders: missing text indicator data." ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG237" ) = "Failed to animate image placeholders: missing text indicator data." ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Failed to animate image placeholders: missing text indicator data." )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Failed to animate image placeholders: missing text indicator data." ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11134 | return; |
| 11135 | } |
| 11136 | |
| 11137 | auto snapshotWithoutSelection = textIndicator->contentImageWithoutSelection(); |
| 11138 | if (!snapshotWithoutSelection) { |
| 11139 | RELEASE_LOG(DragAndDrop, "Failed to animate image placeholders: missing unselected content image.")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Failed to animate image placeholders: missing unselected content image." ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG238" ) = "Failed to animate image placeholders: missing unselected content image." ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Failed to animate image placeholders: missing unselected content image." )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Failed to animate image placeholders: missing unselected content image." ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11140 | return; |
| 11141 | } |
| 11142 | |
| 11143 | auto unselectedSnapshotImage = snapshotWithoutSelection->nativeImage(); |
| 11144 | if (!unselectedSnapshotImage) { |
| 11145 | RELEASE_LOG(DragAndDrop, "Failed to animate image placeholders: could not decode unselected content image.")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Failed to animate image placeholders: could not decode unselected content image." ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG239" ) = "Failed to animate image placeholders: could not decode unselected content image." ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Failed to animate image placeholders: could not decode unselected content image." )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Failed to animate image placeholders: could not decode unselected content image." ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11146 | return; |
| 11147 | } |
| 11148 | |
| 11149 | auto unselectedContentImageForEditDrag = adoptNS([[UIImage alloc] initWithCGImage:unselectedSnapshotImage->platformImage().get() scale:protect(protectedSelf->_page)->deviceScaleFactor() orientation:UIImageOrientationUp]); |
| 11150 | auto snapshotView = adoptNS([[UIImageView alloc] initWithImage:unselectedContentImageForEditDrag.get()]); |
| 11151 | [snapshotView setFrame:textIndicator->contentImageWithoutSelectionRectInRootViewCoordinates()]; |
| 11152 | [protectedSelf addSubview:snapshotView.get()]; |
| 11153 | protectedSelf->_unselectedContentSnapshot = WTF::move(snapshotView); |
| 11154 | state.deliverDelayedDropPreview(protectedSelf.get(), [protectedSelf unobscuredContentRect], dragItems.get(), placeholderRects); |
| 11155 | }); |
| 11156 | |
| 11157 | return YES__objc_yes; |
| 11158 | } |
| 11159 | |
| 11160 | #pragma mark - UIDragInteractionDelegate |
| 11161 | |
| 11162 | - (BOOL)_dragInteraction:(UIDragInteraction *)interaction shouldDelayCompetingGestureRecognizer:(UIGestureRecognizer *)competingGestureRecognizer |
| 11163 | { |
| 11164 | if (_highlightLongPressGestureRecognizer == competingGestureRecognizer) { |
| 11165 | // Since 3D touch still recognizes alongside the drag lift, and also requires the highlight long press |
| 11166 | // gesture to be active to support cancelling when `touchstart` is prevented, we should also allow the |
| 11167 | // highlight long press to recognize simultaneously, and manually cancel it when the drag lift is |
| 11168 | // recognized (see _dragInteraction:prepareForSession:completion:). |
| 11169 | return NO__objc_no; |
| 11170 | } |
| 11171 | return [competingGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]; |
| 11172 | } |
| 11173 | |
| 11174 | - (NSInteger)_dragInteraction:(UIDragInteraction *)interaction dataOwnerForSession:(id<UIDragSession>)session |
| 11175 | { |
| 11176 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11177 | NSInteger dataOwner = 0; |
| 11178 | if ([uiDelegate respondsToSelector:@selector(_webView:dataOwnerForDragSession:)]) |
| 11179 | dataOwner = [uiDelegate _webView:self.webView dataOwnerForDragSession:session]; |
| 11180 | return dataOwner; |
| 11181 | } |
| 11182 | |
| 11183 | - (void)_dragInteraction:(UIDragInteraction *)interaction itemsForAddingToSession:(id<UIDragSession>)session withTouchAtPoint:(CGPoint)point completion:(void(^)(NSArray<UIDragItem *> *))completion |
| 11184 | { |
| 11185 | if (!_dragDropInteractionState.shouldRequestAdditionalItemForDragSession(session)) { |
| 11186 | completion(@[ ]); |
| 11187 | return; |
| 11188 | } |
| 11189 | |
| 11190 | _dragDropInteractionState.dragSessionWillRequestAdditionalItem(completion); |
| 11191 | protect(_page)->requestAdditionalItemsForDragSession(std::nullopt, WebCore::roundedIntPoint(point), WebCore::roundedIntPoint(point), self._allowedDragSourceActions, [weakSelf = WeakObjCPtr { self }] (bool handled) { |
| 11192 | [weakSelf.get() _didHandleAdditionalDragItemsRequest:handled]; |
| 11193 | }); |
| 11194 | } |
| 11195 | |
| 11196 | - (void)_dragInteraction:(UIDragInteraction *)interaction prepareForSession:(id<UIDragSession>)session completion:(dispatch_block_t)completion |
| 11197 | { |
| 11198 | RELEASE_LOG(DragAndDrop, "Preparing for drag session: %p", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Preparing for drag session: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG240" ) = "Preparing for drag session: %p"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Preparing for drag session: %p", session)]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Preparing for drag session: %p", session), (uint32_t )sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11199 | if (self.currentDragOrDropSession) { |
| 11200 | // FIXME: Support multiple simultaneous drag sessions in the future. |
| 11201 | RELEASE_LOG(DragAndDrop, "Drag session failed: %p (a current drag session already exists)", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session failed: %p (a current drag session already exists)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG241" ) = "Drag session failed: %p (a current drag session already exists)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drag session failed: %p (a current drag session already exists)" , session)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Drag session failed: %p (a current drag session already exists)" , session), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11202 | completion(); |
| 11203 | return; |
| 11204 | } |
| 11205 | |
| 11206 | [self cleanUpDragSourceSessionState]; |
| 11207 | |
| 11208 | auto prepareForSession = [weakSelf = WeakObjCPtr<WKContentView>(self), session = retainPtr(session), completion = makeBlockPtr(completion)] (WebKit::ProceedWithTextSelectionInImage proceedWithTextSelectionInImage) { |
| 11209 | auto strongSelf = weakSelf.get(); |
| 11210 | if (!strongSelf || proceedWithTextSelectionInImage == WebKit::ProceedWithTextSelectionInImage::Yes) { |
| 11211 | completion(); |
| 11212 | return; |
| 11213 | } |
| 11214 | |
| 11215 | auto dragOrigin = [session locationInView:strongSelf.get()]; |
| 11216 | strongSelf->_dragDropInteractionState.prepareForDragSession(session.get(), completion.get()); |
| 11217 | protect(strongSelf->_page)->requestDragStart(std::nullopt, WebCore::roundedIntPoint(dragOrigin), WebCore::roundedIntPoint([strongSelf convertPoint:dragOrigin toView:[strongSelf window]]), [strongSelf _allowedDragSourceActions], [weakSelf = WeakObjCPtr { strongSelf.get() }] (bool started) { |
| 11218 | [weakSelf.get() _didHandleDragStartRequest:started]; |
| 11219 | }); |
| 11220 | RELEASE_LOG(DragAndDrop, "Drag session requested: %p at origin: {%.0f, %.0f}", session.get(), dragOrigin.x, dragOrigin.y)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session requested: %p at origin: {%.0f, %.0f}"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG242" ) = "Drag session requested: %p at origin: {%.0f, %.0f}"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session requested: %p at origin: {%.0f, %.0f}", session .get(), dragOrigin.x, dragOrigin.y)]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Drag session requested: %p at origin: {%.0f, %.0f}" , session.get(), dragOrigin.x, dragOrigin.y), (uint32_t)sizeof (_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11221 | }; |
| 11222 | |
| 11223 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 11224 | [self _doAfterPendingImageAnalysis:prepareForSession]; |
| 11225 | #else |
| 11226 | prepareForSession(WebKit::ProceedWithTextSelectionInImage::No); |
| 11227 | #endif |
| 11228 | } |
| 11229 | |
| 11230 | - (NSArray<UIDragItem *> *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id<UIDragSession>)session |
| 11231 | { |
| 11232 | ASSERT(interaction == _dragInteraction)((void)0); |
| 11233 | RELEASE_LOG(DragAndDrop, "Drag items requested for session: %p", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag items requested for session: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG243" ) = "Drag items requested for session: %p"; uint8_t _Alignas( 16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag items requested for session: %p", session)]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag items requested for session: %p" , session), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11234 | if (_dragDropInteractionState.dragSession() != session) { |
| 11235 | RELEASE_LOG(DragAndDrop, "Drag session failed: %p (delegate session does not match %p)", session, _dragDropInteractionState.dragSession())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session failed: %p (delegate session does not match %p)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG244" ) = "Drag session failed: %p (delegate session does not match %p)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drag session failed: %p (delegate session does not match %p)" , session, _dragDropInteractionState.dragSession())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session failed: %p (delegate session does not match %p)" , session, _dragDropInteractionState.dragSession()), (uint32_t )sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11236 | return @[ ]; |
| 11237 | } |
| 11238 | |
| 11239 | if (!_dragDropInteractionState.hasStagedDragSource()) { |
| 11240 | RELEASE_LOG(DragAndDrop, "Drag session failed: %p (missing staged drag source)", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session failed: %p (missing staged drag source)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG245" ) = "Drag session failed: %p (missing staged drag source)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session failed: %p (missing staged drag source)", session )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session failed: %p (missing staged drag source)" , session), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11241 | return @[ ]; |
| 11242 | } |
| 11243 | |
| 11244 | auto stagedDragSource = _dragDropInteractionState.stagedDragSource(); |
| 11245 | auto *registrationLists = [[WebItemProviderPasteboard sharedInstance] takeRegistrationLists]; |
| 11246 | NSArray *dragItems = [self _itemsForBeginningOrAddingToSessionWithRegistrationLists:registrationLists stagedDragSource:stagedDragSource]; |
| 11247 | if (![dragItems count]) |
| 11248 | protect(_page)->dragCancelled(); |
| 11249 | else |
| 11250 | [self _cancelLongPressGestureRecognizer]; |
| 11251 | |
| 11252 | RELEASE_LOG(DragAndDrop, "Drag session: %p starting with %tu items", session, [dragItems count])clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session: %p starting with %tu items"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG246" ) = "Drag session: %p starting with %tu items"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session: %p starting with %tu items", session, [dragItems count])]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Drag session: %p starting with %tu items", session, [dragItems count]), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11253 | _dragDropInteractionState.clearStagedDragSource([dragItems count] ? WebKit::DragDropInteractionState::DidBecomeActive::Yes : WebKit::DragDropInteractionState::DidBecomeActive::No); |
| 11254 | |
| 11255 | return dragItems; |
| 11256 | } |
| 11257 | |
| 11258 | - (UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id<UIDragSession>)session |
| 11259 | { |
| 11260 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11261 | if ([uiDelegate respondsToSelector:@selector(_webView:previewForLiftingItem:session:)]) { |
| 11262 | UITargetedDragPreview *overriddenPreview = [uiDelegate _webView:self.webView previewForLiftingItem:item session:session]; |
| 11263 | if (overriddenPreview) |
| 11264 | return overriddenPreview; |
| 11265 | } |
| 11266 | return _dragDropInteractionState.previewForLifting(item, self, self.containerForDragPreviews, WTF::move(_positionInformationLinkIndicator)); |
| 11267 | } |
| 11268 | |
| 11269 | - (void)dragInteraction:(UIDragInteraction *)interaction willAnimateLiftWithAnimator:(id<UIDragAnimating>)animator session:(id<UIDragSession>)session |
| 11270 | { |
| 11271 | RELEASE_LOG(DragAndDrop, "Drag session willAnimateLiftWithAnimator: %p", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session willAnimateLiftWithAnimator: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG247" ) = "Drag session willAnimateLiftWithAnimator: %p"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session willAnimateLiftWithAnimator: %p", session)]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session willAnimateLiftWithAnimator: %p" , session), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11272 | if (_dragDropInteractionState.anyActiveDragSourceContainsSelection()) { |
| 11273 | [self cancelActiveTextInteractionGestures]; |
| 11274 | if (!_shouldRestoreEditMenuAfterDrop) { |
| 11275 | [_textInteractionWrapper willBeginDragLift]; |
| 11276 | _shouldRestoreEditMenuAfterDrop = YES__objc_yes; |
| 11277 | } |
| 11278 | } |
| 11279 | |
| 11280 | auto positionForDragEnd = WebCore::roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd()); |
| 11281 | RetainPtr<WKContentView> protectedSelf(self); |
| 11282 | [animator addCompletion:[session = RetainPtr { session }, positionForDragEnd, protectedSelf, page = _page] (UIViewAnimatingPosition finalPosition) { |
| 11283 | #if RELEASE_LOG_DISABLED!((defined 1 && 1) || (defined ENABLE_JOURNALD_LOG && ENABLE_JOURNALD_LOG) || (defined WTF_OS_ANDROID && WTF_OS_ANDROID )) |
| 11284 | UNUSED_PARAM(session)(void)session; |
| 11285 | #endif |
| 11286 | if (finalPosition == UIViewAnimatingPositionStart) { |
| 11287 | RELEASE_LOG(DragAndDrop, "Drag session ended at start: %p", session.get())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session ended at start: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG248" ) = "Drag session ended at start: %p"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session ended at start: %p", session.get())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session ended at start: %p" , session.get()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11288 | // The lift was canceled, so -dropInteraction:sessionDidEnd: will never be invoked. This is the last chance to clean up. |
| 11289 | [protectedSelf cleanUpDragSourceSessionState]; |
| 11290 | page->dragEnded(positionForDragEnd, positionForDragEnd, { }); |
| 11291 | } |
| 11292 | #if !RELEASE_LOG_DISABLED!((defined 1 && 1) || (defined ENABLE_JOURNALD_LOG && ENABLE_JOURNALD_LOG) || (defined WTF_OS_ANDROID && WTF_OS_ANDROID )) |
| 11293 | else |
| 11294 | RELEASE_LOG(DragAndDrop, "Drag session did not end at start: %p", session.get())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session did not end at start: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG249" ) = "Drag session did not end at start: %p"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session did not end at start: %p", session.get())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session did not end at start: %p" , session.get()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11295 | #endif |
| 11296 | }]; |
| 11297 | } |
| 11298 | |
| 11299 | - (void)dragInteraction:(UIDragInteraction *)interaction sessionWillBegin:(id<UIDragSession>)session |
| 11300 | { |
| 11301 | RELEASE_LOG(DragAndDrop, "Drag session beginning: %p", session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session beginning: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG250" ) = "Drag session beginning: %p"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag session beginning: %p", session)]; _os_log_impl(&__dso_handle , _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Drag session beginning: %p", session), (uint32_t )sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11302 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11303 | if ([uiDelegate respondsToSelector:@selector(_webView:dataInteraction:sessionWillBegin:)]) |
| 11304 | [uiDelegate _webView:self.webView dataInteraction:interaction sessionWillBegin:session]; |
| 11305 | |
| 11306 | [protect(_actionSheetAssistant) cleanupSheet]; |
| 11307 | _dragDropInteractionState.dragSessionWillBegin(); |
| 11308 | protect(_page)->didStartDrag(); |
| 11309 | } |
| 11310 | |
| 11311 | - (void)dragInteraction:(UIDragInteraction *)interaction session:(id<UIDragSession>)session didEndWithOperation:(UIDropOperation)operation |
| 11312 | { |
| 11313 | RELEASE_LOG(DragAndDrop, "Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)", session, operation, _dragDropInteractionState.isPerformingDrop(), _dragDropInteractionState.didBeginDragging())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG251" ) = "Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)" , session, operation, _dragDropInteractionState.isPerformingDrop (), _dragDropInteractionState.didBeginDragging())]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag session ended: %p (with operation: %tu, performing operation: %d, began dragging: %d)" , session, operation, _dragDropInteractionState.isPerformingDrop (), _dragDropInteractionState.didBeginDragging()), (uint32_t) sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11314 | |
| 11315 | [self _restoreEditMenuIfNeeded]; |
| 11316 | |
| 11317 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11318 | if ([uiDelegate respondsToSelector:@selector(_webView:dataInteraction:session:didEndWithOperation:)]) |
| 11319 | [uiDelegate _webView:self.webView dataInteraction:interaction session:session didEndWithOperation:operation]; |
| 11320 | |
| 11321 | if (_dragDropInteractionState.isPerformingDrop()) |
| 11322 | return; |
| 11323 | |
| 11324 | [self cleanUpDragSourceSessionState]; |
| 11325 | protect(_page)->dragEnded(WebCore::roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd()), WebCore::roundedIntPoint(_dragDropInteractionState.adjustedPositionForDragEnd()), coreDragOperationForUIDropOperation(operation)); |
| 11326 | } |
| 11327 | |
| 11328 | - (UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForCancellingItem:(UIDragItem *)item withDefault:(UITargetedDragPreview *)defaultPreview |
| 11329 | { |
| 11330 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11331 | if ([uiDelegate respondsToSelector:@selector(_webView:previewForCancellingItem:withDefault:)]) { |
| 11332 | UITargetedDragPreview *overriddenPreview = [uiDelegate _webView:self.webView previewForCancellingItem:item withDefault:defaultPreview]; |
| 11333 | if (overriddenPreview) |
| 11334 | return overriddenPreview; |
| 11335 | } |
| 11336 | return _dragDropInteractionState.previewForCancelling(item, self, self.unscaledView); |
| 11337 | } |
| 11338 | |
| 11339 | - (void)dragInteraction:(UIDragInteraction *)interaction item:(UIDragItem *)item willAnimateCancelWithAnimator:(id<UIDragAnimating>)animator |
| 11340 | { |
| 11341 | _isAnimatingDragCancel = YES__objc_yes; |
| 11342 | RELEASE_LOG(DragAndDrop, "Drag interaction willAnimateCancelWithAnimator")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag interaction willAnimateCancelWithAnimator"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG252" ) = "Drag interaction willAnimateCancelWithAnimator"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drag interaction willAnimateCancelWithAnimator")]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag interaction willAnimateCancelWithAnimator" ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11343 | auto previewViews = _dragDropInteractionState.takePreviewViewsForDragCancel(); |
| 11344 | for (auto& previewView : previewViews) |
| 11345 | [previewView setAlpha:0]; |
| 11346 | |
| 11347 | [animator addCompletion:[protectedSelf = retainPtr(self), previewViews = WTF::move(previewViews), page = _page] (UIViewAnimatingPosition finalPosition) mutable { |
| 11348 | RELEASE_LOG(DragAndDrop, "Drag interaction willAnimateCancelWithAnimator (animation completion block fired)")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drag interaction willAnimateCancelWithAnimator (animation completion block fired)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG253" ) = "Drag interaction willAnimateCancelWithAnimator (animation completion block fired)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drag interaction willAnimateCancelWithAnimator (animation completion block fired)" )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Drag interaction willAnimateCancelWithAnimator (animation completion block fired)" ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11349 | for (auto& previewView : previewViews) |
| 11350 | [previewView setAlpha:1]; |
| 11351 | |
| 11352 | page->dragCancelled(); |
| 11353 | |
| 11354 | page->callAfterNextPresentationUpdate([previewViews = WTF::move(previewViews), protectedSelf = WTF::move(protectedSelf)] { |
| 11355 | for (auto& previewView : previewViews) |
| 11356 | [previewView removeFromSuperview]; |
| 11357 | |
| 11358 | protectedSelf->_isAnimatingDragCancel = NO__objc_no; |
| 11359 | }); |
| 11360 | }]; |
| 11361 | } |
| 11362 | |
| 11363 | - (void)dragInteraction:(UIDragInteraction *)interaction sessionDidTransferItems:(id<UIDragSession>)session |
| 11364 | { |
| 11365 | [protect(existingLocalDragSessionContext(session)) cleanUpTemporaryDirectories]; |
| 11366 | } |
| 11367 | |
| 11368 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 11369 | |
| 11370 | #pragma mark - BEDragInteractionDelegate |
| 11371 | |
| 11372 | - (void)dragInteraction:(BEDragInteraction *)interaction prepareDragSession:(id<UIDragSession>)session completion:(BOOL(^)(void))completion |
| 11373 | { |
| 11374 | [self _dragInteraction:interaction prepareForSession:session completion:[completion = makeBlockPtr(completion)] { |
| 11375 | completion(); |
| 11376 | }]; |
| 11377 | } |
| 11378 | |
| 11379 | - (void)dragInteraction:(BEDragInteraction *)interaction itemsForAddingToSession:(id<UIDragSession>)session forTouchAtPoint:(CGPoint)point completion:(BOOL(^)(NSArray<UIDragItem *> *))completion |
| 11380 | { |
| 11381 | [self _dragInteraction:interaction itemsForAddingToSession:session withTouchAtPoint:point completion:[completion = makeBlockPtr(completion)](NSArray<UIDragItem *> *items) { |
| 11382 | completion(items); |
| 11383 | }]; |
| 11384 | } |
| 11385 | |
| 11386 | #endif // USE(BROWSERENGINEKIT) |
| 11387 | |
| 11388 | #pragma mark - UIDropInteractionDelegate |
| 11389 | |
| 11390 | - (NSInteger)_dropInteraction:(UIDropInteraction *)interaction dataOwnerForSession:(id<UIDropSession>)session |
| 11391 | { |
| 11392 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11393 | NSInteger dataOwner = 0; |
| 11394 | if ([uiDelegate respondsToSelector:@selector(_webView:dataOwnerForDropSession:)]) |
| 11395 | dataOwner = [uiDelegate _webView:self.webView dataOwnerForDropSession:session]; |
| 11396 | return dataOwner; |
| 11397 | } |
| 11398 | |
| 11399 | - (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session |
| 11400 | { |
| 11401 | // FIXME: Support multiple simultaneous drop sessions in the future. |
| 11402 | id<UIDragDropSession> dragOrDropSession = self.currentDragOrDropSession; |
| 11403 | RELEASE_LOG(DragAndDrop, "Can handle drag session: %p with local session: %p existing session: %p?", session, session.localDragSession, dragOrDropSession)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Can handle drag session: %p with local session: %p existing session: %p?" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG254" ) = "Can handle drag session: %p with local session: %p existing session: %p?" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Can handle drag session: %p with local session: %p existing session: %p?" , session, session.localDragSession, dragOrDropSession)]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Can handle drag session: %p with local session: %p existing session: %p?" , session, session.localDragSession, dragOrDropSession), (uint32_t )sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11404 | |
| 11405 | return !dragOrDropSession || session.localDragSession == dragOrDropSession; |
| 11406 | } |
| 11407 | |
| 11408 | - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnter:(id<UIDropSession>)session |
| 11409 | { |
| 11410 | RELEASE_LOG(DragAndDrop, "Drop session entered: %p with %tu items", session, session.items.count)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drop session entered: %p with %tu items"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG255" ) = "Drop session entered: %p with %tu items"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drop session entered: %p with %tu items", session, session. items.count)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Drop session entered: %p with %tu items", session, session .items.count), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11411 | auto dragData = [self dragDataForDropSession:session dragDestinationAction:[self _dragDestinationActionForDropSession:session]]; |
| 11412 | |
| 11413 | _dragDropInteractionState.dropSessionDidEnterOrUpdate(session, dragData); |
| 11414 | |
| 11415 | [[WebItemProviderPasteboard sharedInstance] setItemProviders:protect(extractItemProvidersFromDropSession(session)) dropSession:session]; |
| 11416 | protect(_page)->dragEntered(dragData, WebCore::Pasteboard::nameOfDragPasteboard()); |
| 11417 | } |
| 11418 | |
| 11419 | - (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session |
| 11420 | { |
| 11421 | [[WebItemProviderPasteboard sharedInstance] setItemProviders:protect(extractItemProvidersFromDropSession(session)) dropSession:session]; |
| 11422 | |
| 11423 | Ref page = *_page; |
| 11424 | auto dragData = [self dragDataForDropSession:session dragDestinationAction:[self _dragDestinationActionForDropSession:session]]; |
| 11425 | page->dragUpdated(dragData, WebCore::Pasteboard::nameOfDragPasteboard()); |
| 11426 | _dragDropInteractionState.dropSessionDidEnterOrUpdate(session, dragData); |
| 11427 | |
| 11428 | auto delegate = self.webViewUIDelegate; |
| 11429 | auto operation = dropOperationForWebCoreDragOperation(page->currentDragOperation()); |
| 11430 | if ([delegate respondsToSelector:@selector(_webView:willUpdateDataInteractionOperationToOperation:forSession:)]) |
| 11431 | operation = static_cast<UIDropOperation>([delegate _webView:self.webView willUpdateDataInteractionOperationToOperation:operation forSession:session]); |
| 11432 | |
| 11433 | auto proposal = adoptNS([[UIDropProposal alloc] initWithDropOperation:static_cast<UIDropOperation>(operation)]); |
| 11434 | auto dragHandlingMethod = page->currentDragHandlingMethod(); |
| 11435 | if (dragHandlingMethod == WebCore::DragHandlingMethod::EditPlainText || dragHandlingMethod == WebCore::DragHandlingMethod::EditRichText) { |
| 11436 | // When dragging near the top or bottom edges of an editable element, enabling precision drop mode may result in the drag session hit-testing outside of the editable |
| 11437 | // element, causing the drag to no longer be accepted. This in turn disables precision drop mode, which causes the drag session to hit-test inside of the editable |
| 11438 | // element again, which enables precision mode, thus continuing the cycle. To avoid precision mode thrashing, we forbid precision mode when dragging near the top or |
| 11439 | // bottom of the editable element. |
| 11440 | auto minimumDistanceFromVerticalEdgeForPreciseDrop = 25 / self.webView.scrollView.zoomScale; |
| 11441 | [proposal setPrecise:CGRectContainsPoint(CGRectInset(page->currentDragCaretEditableElementRect(), 0, minimumDistanceFromVerticalEdgeForPreciseDrop), [session locationInView:self])]; |
| 11442 | } else |
| 11443 | [proposal setPrecise:NO__objc_no]; |
| 11444 | |
| 11445 | if ([delegate respondsToSelector:@selector(_webView:willUpdateDropProposalToProposal:forSession:)]) |
| 11446 | proposal = [delegate _webView:self.webView willUpdateDropProposalToProposal:proposal.get() forSession:session]; |
| 11447 | |
| 11448 | return proposal.autorelease(); |
| 11449 | } |
| 11450 | |
| 11451 | - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidExit:(id<UIDropSession>)session |
| 11452 | { |
| 11453 | RELEASE_LOG(DragAndDrop, "Drop session exited: %p with %tu items", session, session.items.count)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drop session exited: %p with %tu items"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG256" ) = "Drop session exited: %p with %tu items"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Drop session exited: %p with %tu items", session, session.items .count)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Drop session exited: %p with %tu items", session, session. items.count), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11454 | [[WebItemProviderPasteboard sharedInstance] setItemProviders:protect(extractItemProvidersFromDropSession(session)) dropSession:session]; |
| 11455 | |
| 11456 | Ref page = *_page; |
| 11457 | auto dragData = [self dragDataForDropSession:session dragDestinationAction:WKDragDestinationActionAny]; |
| 11458 | page->dragExited(dragData); |
| 11459 | page->resetCurrentDragInformation(); |
| 11460 | |
| 11461 | _dragDropInteractionState.dropSessionDidExit(); |
| 11462 | } |
| 11463 | |
| 11464 | - (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session |
| 11465 | { |
| 11466 | RetainPtr itemProviders = extractItemProvidersFromDropSession(session); |
| 11467 | id<WKUIDelegatePrivate> uiDelegate = self.webViewUIDelegate; |
| 11468 | if ([uiDelegate respondsToSelector:@selector(_webView:performDataInteractionOperationWithItemProviders:)]) { |
| 11469 | if ([uiDelegate _webView:self.webView performDataInteractionOperationWithItemProviders:itemProviders.get()]) |
| 11470 | return; |
| 11471 | } |
| 11472 | |
| 11473 | if ([uiDelegate respondsToSelector:@selector(_webView:willPerformDropWithSession:)]) { |
| 11474 | itemProviders = extractItemProvidersFromDragItems([uiDelegate _webView:self.webView willPerformDropWithSession:session]); |
| 11475 | if (!itemProviders.get().count) |
| 11476 | return; |
| 11477 | } |
| 11478 | |
| 11479 | _dragDropInteractionState.dropSessionWillPerformDrop(); |
| 11480 | |
| 11481 | [[WebItemProviderPasteboard sharedInstance] setItemProviders:itemProviders.get() dropSession:session]; |
| 11482 | [[WebItemProviderPasteboard sharedInstance] incrementPendingOperationCount]; |
| 11483 | auto dragData = [self dragDataForDropSession:session dragDestinationAction:WKDragDestinationActionAny]; |
| 11484 | BOOL shouldSnapshotView = ![self _handleDropByInsertingImagePlaceholders:itemProviders.get() session:session]; |
| 11485 | |
| 11486 | RELEASE_LOG(DragAndDrop, "Loading data from %tu item providers for session: %p", itemProviders.get().count, session)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Loading data from %tu item providers for session: %p"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG257" ) = "Loading data from %tu item providers for session: %p"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Loading data from %tu item providers for session: %p", itemProviders .get().count, session)]; _os_log_impl(&__dso_handle, _log_tmp , _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format( _os_fmt_buf, "Loading data from %tu item providers for session: %p" , itemProviders.get().count, session), (uint32_t)sizeof(_os_fmt_buf )) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11487 | // Always loading content from the item provider ensures that the web process will be allowed to call back in to the UI |
| 11488 | // process to access pasteboard contents at a later time. Ideally, we only need to do this work if we're over a file input |
| 11489 | // or the page prevented default on `dragover`, but without this, dropping into a normal editable areas will fail due to |
| 11490 | // item providers not loading any data. |
| 11491 | RetainPtr<WKContentView> retainedSelf(self); |
| 11492 | [[WebItemProviderPasteboard sharedInstance] doAfterLoadingProvidedContentIntoFileURLs:[retainedSelf, capturedDragData = WTF::move(dragData), shouldSnapshotView] (NSArray *fileURLs) mutable { |
| 11493 | RELEASE_LOG(DragAndDrop, "Loaded data into %tu files", fileURLs.count)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Loaded data into %tu files"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG258" ) = "Loaded data into %tu files"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Loaded data into %tu files", fileURLs.count)]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Loaded data into %tu files" , fileURLs.count), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11494 | Vector<String> filenames; |
| 11495 | for (NSURL *fileURL in fileURLs) |
| 11496 | filenames.append([fileURL path]); |
| 11497 | capturedDragData.setFileNames(filenames); |
| 11498 | |
| 11499 | Ref page = *retainedSelf->_page; |
| 11500 | WebKit::SandboxExtensionHandle sandboxExtensionHandle; |
| 11501 | Vector<WebKit::SandboxExtensionHandle> sandboxExtensionForUpload; |
| 11502 | auto dragPasteboardName = WebCore::Pasteboard::nameOfDragPasteboard(); |
| 11503 | page->createSandboxExtensionsIfNeeded(filenames, sandboxExtensionHandle, sandboxExtensionForUpload); |
| 11504 | page->performDragOperation(capturedDragData, dragPasteboardName, WTF::move(sandboxExtensionHandle), WTF::move(sandboxExtensionForUpload)); |
| 11505 | if (shouldSnapshotView) { |
| 11506 | retainedSelf->_visibleContentViewSnapshot = [retainedSelf snapshotViewAfterScreenUpdates:NO__objc_no]; |
| 11507 | [retainedSelf->_visibleContentViewSnapshot setFrame:[retainedSelf bounds]]; |
| 11508 | [retainedSelf addSubview:retainedSelf->_visibleContentViewSnapshot.get()]; |
| 11509 | } |
| 11510 | }]; |
| 11511 | } |
| 11512 | |
| 11513 | - (void)dropInteraction:(UIDropInteraction *)interaction item:(UIDragItem *)item willAnimateDropWithAnimator:(id<UIDragAnimating>)animator |
| 11514 | { |
| 11515 | _dropAnimationCount++; |
| 11516 | [animator addCompletion:[strongSelf = retainPtr(self)] (UIViewAnimatingPosition) { |
| 11517 | if (!--strongSelf->_dropAnimationCount) |
| 11518 | [std::exchange(strongSelf->_unselectedContentSnapshot, nilnullptr) removeFromSuperview]; |
| 11519 | }]; |
| 11520 | } |
| 11521 | |
| 11522 | - (void)dropInteraction:(UIDropInteraction *)interaction concludeDrop:(id<UIDropSession>)session |
| 11523 | { |
| 11524 | [self _removeContainerForDropPreviews]; |
| 11525 | [std::exchange(_visibleContentViewSnapshot, nilnullptr) removeFromSuperview]; |
| 11526 | [std::exchange(_unselectedContentSnapshot, nilnullptr) removeFromSuperview]; |
| 11527 | protect(_page)->didConcludeDrop(); |
| 11528 | } |
| 11529 | |
| 11530 | - (UITargetedDragPreview *)dropInteraction:(UIDropInteraction *)interaction previewForDroppingItem:(UIDragItem *)item withDefault:(UITargetedDragPreview *)defaultPreview |
| 11531 | { |
| 11532 | if (auto preview = _dragDropInteractionState.finalDropPreview(item)) |
| 11533 | return preview; |
| 11534 | |
| 11535 | _dragDropInteractionState.addDefaultDropPreview(item, defaultPreview); |
| 11536 | |
| 11537 | CGRect caretRect = protect(_page)->currentDragCaretRect(); |
| 11538 | if (CGRectIsEmpty(caretRect)) |
| 11539 | return nilnullptr; |
| 11540 | |
| 11541 | UIView *textEffectsWindow = self.textEffectsWindow; |
| 11542 | auto caretRectInWindowCoordinates = [self convertRect:caretRect toCoordinateSpace:textEffectsWindow]; |
| 11543 | auto caretCenterInWindowCoordinates = CGPointMake(CGRectGetMidX(caretRectInWindowCoordinates), CGRectGetMidY(caretRectInWindowCoordinates)); |
| 11544 | auto targetPreviewCenterInWindowCoordinates = CGPointMake(caretCenterInWindowCoordinates.x + defaultPreview.size.width / 2, caretCenterInWindowCoordinates.y + defaultPreview.size.height / 2); |
| 11545 | auto target = adoptNS([[UIDragPreviewTarget alloc] initWithContainer:textEffectsWindow center:targetPreviewCenterInWindowCoordinates transform:CGAffineTransformIdentity]); |
| 11546 | return [defaultPreview retargetedPreviewWithTarget:target.get()]; |
| 11547 | } |
| 11548 | |
| 11549 | - (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnd:(id<UIDropSession>)session |
| 11550 | { |
| 11551 | RELEASE_LOG(DragAndDrop, "Drop session ended: %p (performing operation: %d, began dragging: %d)", session, _dragDropInteractionState.isPerformingDrop(), _dragDropInteractionState.didBeginDragging())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogDragAndDrop .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Drop session ended: %p (performing operation: %d, began dragging: %d)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG259" ) = "Drop session ended: %p (performing operation: %d, began dragging: %d)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Drop session ended: %p (performing operation: %d, began dragging: %d)" , session, _dragDropInteractionState.isPerformingDrop(), _dragDropInteractionState .didBeginDragging())]; _os_log_impl(&__dso_handle, _log_tmp , _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format( _os_fmt_buf, "Drop session ended: %p (performing operation: %d, began dragging: %d)" , session, _dragDropInteractionState.isPerformingDrop(), _dragDropInteractionState .didBeginDragging()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 11552 | if (_dragDropInteractionState.isPerformingDrop()) { |
| 11553 | // In the case where we are performing a drop, wait until after the drop is handled in the web process to reset drag and drop interaction state. |
| 11554 | return; |
| 11555 | } |
| 11556 | |
| 11557 | if (_dragDropInteractionState.didBeginDragging()) { |
| 11558 | // In the case where the content view is a source of drag items, wait until -dragInteraction:session:didEndWithOperation: to reset drag and drop interaction state. |
| 11559 | return; |
| 11560 | } |
| 11561 | |
| 11562 | CGPoint global; |
| 11563 | CGPoint client; |
| 11564 | [self computeClientAndGlobalPointsForDropSession:session outClientPoint:&client outGlobalPoint:&global]; |
| 11565 | [self cleanUpDragSourceSessionState]; |
| 11566 | protect(_page)->dragEnded(WebCore::roundedIntPoint(client), WebCore::roundedIntPoint(global), { }); |
| 11567 | } |
| 11568 | |
| 11569 | #endif // ENABLE(DRAG_SUPPORT) |
| 11570 | |
| 11571 | #if HAVE(UITOOLTIPINTERACTION)(defined HAVE_UITOOLTIPINTERACTION && HAVE_UITOOLTIPINTERACTION ) |
| 11572 | - (void)_toolTipChanged:(NSString *)newToolTip |
| 11573 | { |
| 11574 | if (!_toolTip) { |
| 11575 | _toolTip = adoptNS([[UIToolTipInteraction alloc] init]); |
| 11576 | [_toolTip setDelegate:self]; |
| 11577 | [self addInteraction:_toolTip.get()]; |
| 11578 | } |
| 11579 | |
| 11580 | // FIXME: rdar://156729915 ([Catalyst] Cannot update tooltip interaction on the same view) |
| 11581 | [_toolTip setDefaultToolTip:newToolTip]; |
| 11582 | RetainPtr sceneView = [[UINSSharedApplicationDelegate() hostWindowForUIWindow:[self window]] sceneView]; |
| 11583 | [[sceneView _dynamicToolTipManager] windowChangedKeyState]; |
| 11584 | } |
| 11585 | |
| 11586 | // MARK: UIToolTipInteractionDelegate |
| 11587 | |
| 11588 | - (UIToolTipConfiguration *)toolTipInteraction:(UIToolTipInteraction *)interaction configurationAtPoint:(CGPoint)point |
| 11589 | { |
| 11590 | return [UIToolTipConfiguration configurationWithToolTip:interaction.defaultToolTip]; |
| 11591 | } |
| 11592 | #endif |
| 11593 | |
| 11594 | #pragma mark - Model Interaction Support |
| 11595 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 11596 | - (void)modelInteractionPanGestureDidBeginAtPoint:(CGPoint)inputPoint |
| 11597 | { |
| 11598 | if (!_stageModeSession) { |
| 11599 | _stageModeSession = { { } }; |
| 11600 | _page->requestInteractiveModelElementAtPoint(WebCore::roundedIntPoint(inputPoint)); |
| 11601 | } |
| 11602 | } |
| 11603 | |
| 11604 | - (void)modelInteractionPanGestureDidUpdateWithPoint:(CGPoint)inputPoint |
| 11605 | { |
| 11606 | if (_stageModeSession && !_stageModeSession->isPreparingForInteraction) { |
| 11607 | WebCore::TransformationMatrix transform; |
| 11608 | transform.translate3d(inputPoint.x, inputPoint.y, 0); |
| 11609 | _stageModeSession->transform = transform; |
| 11610 | _page->stageModeSessionDidUpdate(_stageModeSession->nodeID, _stageModeSession->transform); |
| 11611 | } |
| 11612 | } |
| 11613 | |
| 11614 | - (void)modelInteractionPanGestureDidEnd |
| 11615 | { |
| 11616 | if (_stageModeSession && !_stageModeSession->isPreparingForInteraction) |
| 11617 | _page->stageModeSessionDidEnd(_stageModeSession->nodeID); |
| 11618 | |
| 11619 | [self cleanUpStageModeSessionState]; |
| 11620 | } |
| 11621 | |
| 11622 | - (void)didReceiveInteractiveModelElement:(std::optional<WebCore::NodeIdentifier>)nodeID |
| 11623 | { |
| 11624 | if (!_stageModeSession || !_stageModeSession->isPreparingForInteraction) |
| 11625 | return; |
| 11626 | |
| 11627 | _stageModeSession->isPreparingForInteraction = !nodeID; |
| 11628 | _stageModeSession->nodeID = nodeID; |
| 11629 | } |
| 11630 | |
| 11631 | - (void)cleanUpStageModeSessionState |
| 11632 | { |
| 11633 | _stageModeSession = std::nullopt; |
| 11634 | } |
| 11635 | #endif |
| 11636 | |
| 11637 | - (void)cancelActiveTextInteractionGestures |
| 11638 | { |
| 11639 | [self.textInteractionLoupeGestureRecognizer _wk_cancel]; |
| 11640 | } |
| 11641 | |
| 11642 | - (UIView *)textEffectsWindow |
| 11643 | { |
| 11644 | return [UITextEffectsWindow sharedTextEffectsWindowForWindowScene:self.window.windowScene]; |
| 11645 | } |
| 11646 | |
| 11647 | - (NSDictionary *)_autofillContext |
| 11648 | { |
| 11649 | if (!self._hasFocusedElement) |
| 11650 | return nilnullptr; |
| 11651 | |
| 11652 | auto context = adoptNS([[NSMutableDictionary alloc] init]); |
| 11653 | context.get()[@"_WKAutofillContextVersion"] = @(2); |
| 11654 | |
| 11655 | if (_focusRequiresStrongPasswordAssistance && _focusedElementInformation.elementType == WebKit::InputType::Password) { |
| 11656 | context.get()[@"_automaticPasswordKeyboard"] = @YES__objc_yes; |
| 11657 | context.get()[@"strongPasswordAdditionalContext"] = _additionalContextForStrongPasswordAssistance.get(); |
| 11658 | } else if (_focusedElementInformation.acceptsAutofilledLoginCredentials) |
| 11659 | context.get()[@"_acceptsLoginCredentials"] = @YES__objc_yes; |
| 11660 | |
| 11661 | if (RetainPtr platformURL = _focusedElementInformation.representingPageURL.createNSURL()) |
| 11662 | context.get()[@"_WebViewURL"] = platformURL.get(); |
| 11663 | |
| 11664 | if (_focusedElementInformation.nonAutofillCredentialType == WebCore::NonAutofillCredentialType::WebAuthn) { |
| 11665 | context.get()[@"_page_id"] = [NSNumber numberWithUnsignedLong:_page->webPageIDInMainFrameProcess().toUInt64()]; |
| 11666 | context.get()[@"_frame_id"] = [NSNumber numberWithUnsignedLong:_focusedElementInformation.frame ? _focusedElementInformation.frame->frameID.toUInt64() : 0]; |
| 11667 | context.get()[@"_credential_type"] = WebCore::nonAutofillCredentialTypeString(_focusedElementInformation.nonAutofillCredentialType).createNSString().get(); |
| 11668 | } |
| 11669 | return context.autorelease(); |
| 11670 | } |
| 11671 | |
| 11672 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 11673 | |
| 11674 | static RetainPtr<UIImage> uiImageForImage(WebCore::Image* image) |
| 11675 | { |
| 11676 | if (!image) |
| 11677 | return nilnullptr; |
| 11678 | |
| 11679 | auto nativeImage = image->nativeImage(); |
| 11680 | if (!nativeImage) |
| 11681 | return nilnullptr; |
| 11682 | |
| 11683 | return adoptNS([[UIImage alloc] initWithCGImage:nativeImage->platformImage().get()]); |
| 11684 | } |
| 11685 | |
| 11686 | // FIXME: This should be merged with createTargetedDragPreview in DragDropInteractionState. |
| 11687 | static RetainPtr<UITargetedPreview> createTargetedPreview(UIImage *image, UIView *rootView, UIView *previewContainer, const WebCore::FloatRect& frameInRootViewCoordinates, const Vector<WebCore::FloatRect>& clippingRectsInFrameCoordinates, UIColor *backgroundColor) |
| 11688 | { |
| 11689 | if (frameInRootViewCoordinates.isEmpty() || !image || !previewContainer.window) |
| 11690 | return nilnullptr; |
| 11691 | |
| 11692 | WebCore::FloatRect frameInContainerCoordinates = [rootView convertRect:frameInRootViewCoordinates toView:previewContainer]; |
| 11693 | if (frameInContainerCoordinates.isEmpty()) |
| 11694 | return nilnullptr; |
| 11695 | |
| 11696 | auto scalingRatio = frameInContainerCoordinates.size() / frameInRootViewCoordinates.size(); |
| 11697 | auto clippingRectValuesInFrameCoordinates = createNSArray(clippingRectsInFrameCoordinates, [&] (WebCore::FloatRect rect) { |
| 11698 | rect.scale(scalingRatio); |
| 11699 | return [NSValue valueWithCGRect:rect]; |
| 11700 | }); |
| 11701 | |
| 11702 | RetainPtr<UIPreviewParameters> parameters; |
| 11703 | if ([clippingRectValuesInFrameCoordinates count]) |
| 11704 | parameters = adoptNS([[UIPreviewParameters alloc] initWithTextLineRects:clippingRectValuesInFrameCoordinates.get()]); |
| 11705 | else |
| 11706 | parameters = adoptNS([[UIPreviewParameters alloc] init]); |
| 11707 | |
| 11708 | [parameters setBackgroundColor:protect(backgroundColor ?: [UIColor clearColor])]; |
| 11709 | |
| 11710 | CGPoint centerInContainerCoordinates = { CGRectGetMidX(frameInContainerCoordinates), CGRectGetMidY(frameInContainerCoordinates) }; |
| 11711 | auto target = adoptNS([[UIPreviewTarget alloc] initWithContainer:previewContainer center:centerInContainerCoordinates]); |
| 11712 | |
| 11713 | auto imageView = adoptNS([[UIImageView alloc] initWithImage:image]); |
| 11714 | [imageView setFrame:frameInContainerCoordinates]; |
| 11715 | return adoptNS([[UITargetedPreview alloc] initWithView:imageView.get() parameters:parameters.get() target:target.get()]); |
| 11716 | } |
| 11717 | |
| 11718 | - (UITargetedPreview *)_createTargetedPreviewFromTextIndicator:(RefPtr<WebCore::TextIndicator>&&)textIndicator previewContainer:(UIView *)previewContainer |
| 11719 | { |
| 11720 | if (!textIndicator) |
| 11721 | return nilnullptr; |
| 11722 | |
| 11723 | RetainPtr textIndicatorImage = uiImageForImage(protect(textIndicator->contentImage())); |
| 11724 | RetainPtr preview = createTargetedPreview(textIndicatorImage.get(), self, previewContainer, textIndicator->textBoundingRectInRootViewCoordinates(), textIndicator->textRectsInBoundingRectCoordinates(), protect(^{ |
| 11725 | if (textIndicator->estimatedBackgroundColor() != WebCore::Color::transparentBlack) |
| 11726 | return cocoaColor(textIndicator->estimatedBackgroundColor()).autorelease(); |
| 11727 | |
| 11728 | // In the case where background color estimation fails, it doesn't make sense to |
| 11729 | // show a text indicator preview with a clear background in light mode. Default |
| 11730 | // to the system background color instead. |
| 11731 | return UIColor.systemBackgroundColor; |
| 11732 | }())); |
| 11733 | |
| 11734 | return preview.autorelease(); |
| 11735 | } |
| 11736 | |
| 11737 | static RetainPtr<UITargetedPreview> createFallbackTargetedPreview(UIView *rootView, UIView *containerView, const WebCore::FloatRect& frameInRootViewCoordinates, UIColor *backgroundColor) |
| 11738 | { |
| 11739 | if (!containerView.window) |
| 11740 | return nilnullptr; |
| 11741 | |
| 11742 | if (frameInRootViewCoordinates.isEmpty()) |
| 11743 | return nilnullptr; |
| 11744 | |
| 11745 | auto parameters = adoptNS([[UIPreviewParameters alloc] init]); |
| 11746 | if (backgroundColor) |
| 11747 | [parameters setBackgroundColor:backgroundColor]; |
| 11748 | |
| 11749 | RetainPtr snapshotView = [rootView resizableSnapshotViewFromRect:frameInRootViewCoordinates afterScreenUpdates:NO__objc_no withCapInsets:UIEdgeInsetsZero]; |
| 11750 | if (!snapshotView) |
| 11751 | snapshotView = adoptNS([UIView new]); |
| 11752 | |
| 11753 | CGRect frameInContainerViewCoordinates = [rootView convertRect:frameInRootViewCoordinates toView:containerView]; |
| 11754 | |
| 11755 | if (CGRectIsEmpty(frameInContainerViewCoordinates)) |
| 11756 | return nilnullptr; |
| 11757 | |
| 11758 | [snapshotView setFrame:frameInContainerViewCoordinates]; |
| 11759 | |
| 11760 | CGPoint centerInContainerViewCoordinates = CGPointMake(CGRectGetMidX(frameInContainerViewCoordinates), CGRectGetMidY(frameInContainerViewCoordinates)); |
| 11761 | auto target = adoptNS([[UIPreviewTarget alloc] initWithContainer:containerView center:centerInContainerViewCoordinates]); |
| 11762 | |
| 11763 | return adoptNS([[UITargetedPreview alloc] initWithView:snapshotView.get() parameters:parameters.get() target:target.get()]); |
| 11764 | } |
| 11765 | |
| 11766 | - (UITargetedPreview *)_createTargetedContextMenuHintPreviewForFocusedElement:(WebKit::TargetedPreviewPositioning)positioning |
| 11767 | { |
| 11768 | RetainPtr backgroundColor = [&]() -> UIColor * { |
| 11769 | switch (_focusedElementInformation.elementType) { |
| 11770 | case WebKit::InputType::Date: |
| 11771 | case WebKit::InputType::Month: |
| 11772 | case WebKit::InputType::DateTimeLocal: |
| 11773 | case WebKit::InputType::Time: |
| 11774 | #if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 1 && 1) |
| 11775 | case WebKit::InputType::Week: |
| 11776 | #endif |
| 11777 | return UIColor.clearColor; |
| 11778 | default: |
| 11779 | return nilnullptr; |
| 11780 | } |
| 11781 | }(); |
| 11782 | |
| 11783 | auto previewRect = _focusedElementInformation.interactionRect; |
| 11784 | if (positioning == WebKit::TargetedPreviewPositioning::LeadingOrTrailingEdge) { |
| 11785 | static constexpr auto defaultMenuWidth = 250; |
| 11786 | auto unobscuredRect = WebCore::IntRect { self.unobscuredContentRect }; |
| 11787 | std::optional<int> previewOffsetX; |
| 11788 | |
| 11789 | auto leftEdge = previewRect.x() - defaultMenuWidth; |
| 11790 | bool hasSpaceAfterRightEdge = previewRect.maxX() + defaultMenuWidth <= unobscuredRect.maxX(); |
| 11791 | bool hasSpaceBeforeLeftEdge = leftEdge > unobscuredRect.x(); |
| 11792 | |
| 11793 | switch (self.effectiveUserInterfaceLayoutDirection) { |
| 11794 | case UIUserInterfaceLayoutDirectionLeftToRight: |
| 11795 | if (hasSpaceAfterRightEdge) |
| 11796 | previewOffsetX = previewRect.maxX(); |
| 11797 | else if (hasSpaceBeforeLeftEdge) |
| 11798 | previewOffsetX = leftEdge; |
| 11799 | break; |
| 11800 | case UIUserInterfaceLayoutDirectionRightToLeft: |
| 11801 | if (hasSpaceBeforeLeftEdge) |
| 11802 | previewOffsetX = leftEdge; |
| 11803 | else if (hasSpaceAfterRightEdge) |
| 11804 | previewOffsetX = previewRect.maxX(); |
| 11805 | break; |
| 11806 | } |
| 11807 | |
| 11808 | if (previewOffsetX) { |
| 11809 | static constexpr auto additionalOffsetToDockPresentedMenuToEdge = 20; |
| 11810 | previewRect.setX(additionalOffsetToDockPresentedMenuToEdge + *previewOffsetX); |
| 11811 | previewRect.setWidth(1); |
| 11812 | } |
| 11813 | } |
| 11814 | |
| 11815 | auto targetedPreview = createFallbackTargetedPreview(self, self.containerForContextMenuHintPreviews, previewRect, backgroundColor.get()); |
| 11816 | |
| 11817 | [self _updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:_focusedElementInformation.containerScrollingNodeID]; |
| 11818 | |
| 11819 | _contextMenuInteractionTargetedPreview = WTF::move(targetedPreview); |
| 11820 | return _contextMenuInteractionTargetedPreview.get(); |
| 11821 | } |
| 11822 | |
| 11823 | - (BOOL)positionInformationHasImageOverlayDataDetector |
| 11824 | { |
| 11825 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 11826 | return _positionInformation.isImageOverlayText && [_positionInformation.dataDetectorResults count]; |
| 11827 | #else |
| 11828 | return NO__objc_no; |
| 11829 | #endif |
| 11830 | } |
| 11831 | |
| 11832 | - (UITargetedPreview *)_createTargetedContextMenuHintPreviewIfPossible |
| 11833 | { |
| 11834 | RetainPtr<UIView> containerForContextMenuHintPreviews = self.containerForContextMenuHintPreviews; |
| 11835 | if (![containerForContextMenuHintPreviews window]) |
| 11836 | return nilnullptr; |
| 11837 | |
| 11838 | RetainPtr<UITargetedPreview> targetedPreview; |
| 11839 | |
| 11840 | if (_positionInformation.isLink && _positionInformation.textIndicator && _positionInformation.textIndicator->contentImage()) { |
| 11841 | RefPtr textIndicator = _positionInformation.textIndicator; |
| 11842 | _positionInformationLinkIndicator = textIndicator; |
| 11843 | |
| 11844 | targetedPreview = [self _createTargetedPreviewFromTextIndicator:WTF::move(textIndicator) previewContainer:containerForContextMenuHintPreviews.get()]; |
| 11845 | } else if ((_positionInformation.isAttachment || _positionInformation.isImage) && _positionInformation.image) { |
| 11846 | RetainPtr cgImage = protect(_positionInformation.image)->createPlatformImage(); |
| 11847 | auto image = adoptNS([[UIImage alloc] initWithCGImage:cgImage.get()]); |
| 11848 | targetedPreview = createTargetedPreview(image.get(), self, containerForContextMenuHintPreviews.get(), _positionInformation.bounds, { }, nilnullptr); |
| 11849 | } |
| 11850 | |
| 11851 | if (!targetedPreview) { |
| 11852 | auto boundsForFallbackPreview = [&] { |
| 11853 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 11854 | if (self.positionInformationHasImageOverlayDataDetector) |
| 11855 | return _positionInformation.dataDetectorBounds; |
| 11856 | #endif |
| 11857 | return _positionInformation.bounds; |
| 11858 | }(); |
| 11859 | |
| 11860 | targetedPreview = createFallbackTargetedPreview(self, containerForContextMenuHintPreviews.get(), boundsForFallbackPreview, nilnullptr); |
| 11861 | } |
| 11862 | |
| 11863 | [self _updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:_positionInformation.containerScrollingNodeID]; |
| 11864 | |
| 11865 | _contextMenuInteractionTargetedPreview = WTF::move(targetedPreview); |
| 11866 | return _contextMenuInteractionTargetedPreview.get(); |
| 11867 | } |
| 11868 | |
| 11869 | - (void)_removeContextMenuHintContainerIfPossible |
| 11870 | { |
| 11871 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) |
| 11872 | // If a new _contextMenuElementInfo is installed, we've started another interaction, |
| 11873 | // and removing the hint container view will cause the animation to break. |
| 11874 | if (_contextMenuElementInfo) |
| 11875 | return; |
| 11876 | #endif |
| 11877 | if (_isDisplayingContextMenuWithAnimation) |
| 11878 | return; |
| 11879 | |
| 11880 | if (_isPreparingToDisplayContextMenu) |
| 11881 | return; |
| 11882 | |
| 11883 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 11884 | // We are also using this container for the action sheet assistant... |
| 11885 | if ([protect(_actionSheetAssistant) hasContextMenuInteraction]) |
| 11886 | return; |
| 11887 | #endif |
| 11888 | // and for the file upload panel... |
| 11889 | if (_fileUploadPanel) |
| 11890 | return; |
| 11891 | |
| 11892 | // and for the date/time picker. |
| 11893 | if ([self dateTimeInputControl]) |
| 11894 | return; |
| 11895 | |
| 11896 | if ([self selectControl]) |
| 11897 | return; |
| 11898 | |
| 11899 | if ([_contextMenuHintContainerView subviews].count) |
| 11900 | return; |
| 11901 | |
| 11902 | [self _removeContainerForContextMenuHintPreviews]; |
| 11903 | } |
| 11904 | |
| 11905 | #endif // USE(UICONTEXTMENU) |
| 11906 | |
| 11907 | #if HAVE(UI_WK_DOCUMENT_CONTEXT)(defined 1 && 1) |
| 11908 | |
| 11909 | static inline OptionSet<WebKit::DocumentEditingContextRequest::Options> toWebDocumentRequestOptions(WKBETextDocumentRequestOptionsBETextDocumentRequestOptions flags) |
| 11910 | { |
| 11911 | OptionSet<WebKit::DocumentEditingContextRequest::Options> options; |
| 11912 | |
| 11913 | if (flags & WKBETextDocumentRequestOptionTextBETextDocumentOptionText) |
| 11914 | options.add(WebKit::DocumentEditingContextRequest::Options::Text); |
| 11915 | if (flags & WKBETextDocumentRequestOptionAttributedTextBETextDocumentOptionAttributedText) |
| 11916 | options.add(WebKit::DocumentEditingContextRequest::Options::AttributedText); |
| 11917 | if (flags & WKBETextDocumentRequestOptionTextRectsBETextDocumentOptionTextRects) |
| 11918 | options.add(WebKit::DocumentEditingContextRequest::Options::Rects); |
| 11919 | if (flags & UIWKDocumentRequestSpatial) |
| 11920 | options.add(WebKit::DocumentEditingContextRequest::Options::Spatial); |
| 11921 | if (flags & UIWKDocumentRequestAnnotation) |
| 11922 | options.add(WebKit::DocumentEditingContextRequest::Options::Annotation); |
| 11923 | if (flags & WKBETextDocumentRequestOptionMarkedTextRectsBETextDocumentOptionMarkedTextRects) |
| 11924 | options.add(WebKit::DocumentEditingContextRequest::Options::MarkedTextRects); |
| 11925 | if (flags & UIWKDocumentRequestSpatialAndCurrentSelection) |
| 11926 | options.add(WebKit::DocumentEditingContextRequest::Options::SpatialAndCurrentSelection); |
| 11927 | #if HAVE(AUTOCORRECTION_ENHANCEMENTS)(defined 1 && 1) |
| 11928 | if (flags & WKBETextDocumentRequestOptionAutocorrectedRangesBETextDocumentOptionAutocorrectedRanges) |
| 11929 | options.add(WebKit::DocumentEditingContextRequest::Options::AutocorrectedRanges); |
| 11930 | #endif |
| 11931 | |
| 11932 | return options; |
| 11933 | } |
| 11934 | |
| 11935 | static WebKit::DocumentEditingContextRequest toWebRequest(id request) |
| 11936 | { |
| 11937 | WKBETextDocumentRequestOptionsBETextDocumentRequestOptions options = WKBETextDocumentRequestOptionNoneBETextDocumentOptionNone; |
| 11938 | CGRect documentRect; |
| 11939 | |
| 11940 | if (auto uiRequest = dynamic_objc_cast<UIWKDocumentRequest>(request)) { |
| 11941 | documentRect = uiRequest.documentRect; |
| 11942 | options = static_cast<WKBETextDocumentRequestOptionsBETextDocumentRequestOptions>(uiRequest.flags); |
| 11943 | } |
| 11944 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 11945 | else if (auto seRequest = dynamic_objc_cast<WKBETextDocumentRequestBETextDocumentRequest>(request)) { |
| 11946 | documentRect = seRequest._documentRect; |
| 11947 | options = seRequest.options; |
| 11948 | } |
| 11949 | #endif |
| 11950 | WebKit::DocumentEditingContextRequest webRequest = { |
| 11951 | .options = toWebDocumentRequestOptions(options), |
| 11952 | .surroundingGranularity = toWKTextGranularity([request surroundingGranularity]), |
| 11953 | .granularityCount = [request granularityCount], |
| 11954 | .rect = documentRect |
| 11955 | }; |
| 11956 | if (auto textInputContext = dynamic_objc_cast<_WKTextInputContext>([request inputElementIdentifier])) |
| 11957 | webRequest.textInputContext = [textInputContext _textInputContext]; |
| 11958 | |
| 11959 | return webRequest; |
| 11960 | } |
| 11961 | |
| 11962 | - (void)adjustSelectionWithDelta:(NSRange)deltaRange completionHandler:(void (^)(void))completionHandler |
| 11963 | { |
| 11964 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG260" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(11964, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 11965 | |
| 11966 | // UIKit is putting casted signed integers into NSRange. Cast them back to reveal any negative values. |
| 11967 | [self _internalAdjustSelectionWithOffset:static_cast<NSInteger>(deltaRange.location) lengthDelta:static_cast<NSInteger>(deltaRange.length) completionHandler:completionHandler]; |
| 11968 | } |
| 11969 | |
| 11970 | // The completion handler is called with either a UIWKDocumentContext, or a WKBETextDocumentContext. |
| 11971 | - (void)requestDocumentContext:(id)request completionHandler:(void (^)(NSObject *))completionHandler |
| 11972 | { |
| 11973 | auto webRequest = toWebRequest(request); |
| 11974 | auto options = webRequest.options; |
| 11975 | protect(_page)->requestDocumentEditingContext(WTF::move(webRequest), [useAsyncInteractions = self.shouldUseAsyncInteractions, completionHandler = makeBlockPtr(completionHandler), options] (auto&& editingContext) { |
| 11976 | completionHandler(useAsyncInteractions ? editingContext.toPlatformContext(options) : editingContext.toLegacyPlatformContext(options)); |
| 11977 | }); |
| 11978 | } |
| 11979 | |
| 11980 | - (void)selectPositionAtPoint:(CGPoint)point withContextRequest:(WKBETextDocumentRequestBETextDocumentRequest *)request completionHandler:(void (^)(NSObject *))completionHandler |
| 11981 | { |
| 11982 | logTextInteraction(__PRETTY_FUNCTION__, self.textInteractionLoupeGestureRecognizer, point); |
| 11983 | |
| 11984 | // FIXME: Reduce to 1 message. |
| 11985 | [self selectPositionAtPoint:point completionHandler:[strongSelf = retainPtr(self), request = retainPtr(request), completionHandler = makeBlockPtr(completionHandler)]() mutable { |
| 11986 | [strongSelf requestDocumentContext:request.get() completionHandler:[completionHandler = WTF::move(completionHandler)](NSObject *context) { |
| 11987 | completionHandler(context); |
| 11988 | }]; |
| 11989 | }]; |
| 11990 | } |
| 11991 | |
| 11992 | #endif // HAVE(UI_WK_DOCUMENT_CONTEXT) |
| 11993 | |
| 11994 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 11995 | |
| 11996 | - (void)dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:(id)controller |
| 11997 | { |
| 11998 | BOOL shouldRevealFocusOverlay = NO__objc_no; |
| 11999 | // In the case where there's nothing the user could potentially do besides dismiss the overlay, we can just automatically without asking the delegate. |
| 12000 | if ([self.webView._inputDelegate respondsToSelector:@selector(_webView:shouldRevealFocusOverlayForInputSession:)] |
| 12001 | && ([self actionNameForFocusedFormControlView:_focusedFormControlView.get()] || _focusedElementInformation.hasNextNode || _focusedElementInformation.hasPreviousNode)) |
| 12002 | shouldRevealFocusOverlay = [self.webView._inputDelegate _webView:self.webView shouldRevealFocusOverlayForInputSession:_formInputSession.get()]; |
| 12003 | |
| 12004 | if (shouldRevealFocusOverlay) { |
| 12005 | [protect(_focusedFormControlView) show:NO__objc_no]; |
| 12006 | [self updateCurrentFocusedElementInformation:[weakSelf = WeakObjCPtr<WKContentView>(self)] (bool didUpdate) { |
| 12007 | if (!didUpdate) |
| 12008 | return; |
| 12009 | |
| 12010 | auto focusedFormController = weakSelf.get()->_focusedFormControlView; |
| 12011 | [focusedFormController reloadData:YES__objc_yes]; |
| 12012 | [focusedFormController engageFocusedFormControlNavigation]; |
| 12013 | }]; |
| 12014 | } else |
| 12015 | _page->blurFocusedElement(); |
| 12016 | |
| 12017 | bool shouldDismissViewController = [controller isKindOfClass:UIViewController.class] |
| 12018 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 12019 | && !_presentedQuickboardController |
| 12020 | #endif |
| 12021 | && controller != _presentedFullScreenInputViewController; |
| 12022 | // The Quickboard view controller passed into this delegate method is not necessarily the view controller we originally presented; |
| 12023 | // this happens in the case when the user chooses an input method (e.g. scribble) and a new Quickboard view controller is presented. |
| 12024 | if (shouldDismissViewController) |
| 12025 | [(UIViewController *)controller dismissViewControllerAnimated:YES__objc_yes completion:nilnullptr]; |
| 12026 | |
| 12027 | [self dismissAllInputViewControllers:controller == _presentedFullScreenInputViewController]; |
| 12028 | } |
| 12029 | |
| 12030 | - (UITextContentType)textContentTypeForQuickboard |
| 12031 | { |
| 12032 | switch (_focusedElementInformation.elementType) { |
| 12033 | case WebKit::InputType::Password: |
| 12034 | return UITextContentTypePassword; |
| 12035 | case WebKit::InputType::URL: |
| 12036 | return UITextContentTypeURL; |
| 12037 | case WebKit::InputType::Email: |
| 12038 | return UITextContentTypeEmailAddress; |
| 12039 | case WebKit::InputType::Phone: |
| 12040 | return UITextContentTypeTelephoneNumber; |
| 12041 | default: |
| 12042 | // The element type alone is insufficient to infer content type; fall back to autofill data. |
| 12043 | if (auto contentType = [self contentTypeFromFieldName:_focusedElementInformation.autofillFieldName]) |
| 12044 | return contentType; |
| 12045 | |
| 12046 | if (_focusedElementInformation.isAutofillableUsernameField) |
| 12047 | return UITextContentTypeUsername; |
| 12048 | |
| 12049 | return nilnullptr; |
| 12050 | } |
| 12051 | } |
| 12052 | |
| 12053 | #pragma mark - PUICQuickboardViewControllerDelegate |
| 12054 | |
| 12055 | - (void)quickboard:(PUICQuickboardViewController *)quickboard textEntered:(NSAttributedString *)attributedText |
| 12056 | { |
| 12057 | if (attributedText) |
| 12058 | _page->setTextAsync(attributedText.string); |
| 12059 | |
| 12060 | [self dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:quickboard]; |
| 12061 | } |
| 12062 | |
| 12063 | - (void)quickboardInputCancelled:(PUICQuickboardViewController *)quickboard |
| 12064 | { |
| 12065 | [self dismissQuickboardViewControllerAndRevealFocusedFormOverlayIfNecessary:quickboard]; |
| 12066 | } |
| 12067 | |
| 12068 | #pragma mark - WKQuickboardViewControllerDelegate |
| 12069 | |
| 12070 | - (BOOL)allowsLanguageSelectionForListViewController:(PUICQuickboardViewController *)controller |
| 12071 | { |
| 12072 | switch (_focusedElementInformation.elementType) { |
| 12073 | case WebKit::InputType::ContentEditable: |
| 12074 | case WebKit::InputType::Text: |
| 12075 | case WebKit::InputType::TextArea: |
| 12076 | case WebKit::InputType::Search: |
| 12077 | case WebKit::InputType::Email: |
| 12078 | case WebKit::InputType::URL: |
| 12079 | return YES__objc_yes; |
| 12080 | default: |
| 12081 | return NO__objc_no; |
| 12082 | } |
| 12083 | } |
| 12084 | |
| 12085 | - (UIView *)inputContextViewForViewController:(PUICQuickboardViewController *)controller |
| 12086 | { |
| 12087 | id <_WKInputDelegate> delegate = self.webView._inputDelegate; |
| 12088 | if (![delegate respondsToSelector:@selector(_webView:focusedElementContextViewForInputSession:)]) |
| 12089 | return nilnullptr; |
| 12090 | |
| 12091 | return [delegate _webView:self.webView focusedElementContextViewForInputSession:_formInputSession.get()]; |
| 12092 | } |
| 12093 | |
| 12094 | - (NSString *)inputLabelTextForViewController:(PUICQuickboardViewController *)controller |
| 12095 | { |
| 12096 | return [self inputLabelText]; |
| 12097 | } |
| 12098 | |
| 12099 | - (NSString *)initialValueForViewController:(PUICQuickboardViewController *)controller |
| 12100 | { |
| 12101 | return _focusedElementInformation.value.createNSString().autorelease(); |
| 12102 | } |
| 12103 | |
| 12104 | - (BOOL)shouldDisplayInputContextViewForListViewController:(PUICQuickboardViewController *)controller |
| 12105 | { |
| 12106 | switch (_focusedElementInformation.elementType) { |
| 12107 | case WebKit::InputType::ContentEditable: |
| 12108 | case WebKit::InputType::Text: |
| 12109 | case WebKit::InputType::Password: |
| 12110 | case WebKit::InputType::TextArea: |
| 12111 | case WebKit::InputType::Search: |
| 12112 | case WebKit::InputType::Email: |
| 12113 | case WebKit::InputType::URL: |
| 12114 | case WebKit::InputType::Phone: |
| 12115 | return YES__objc_yes; |
| 12116 | default: |
| 12117 | return NO__objc_no; |
| 12118 | } |
| 12119 | } |
| 12120 | |
| 12121 | #pragma mark - WKTextInputListViewControllerDelegate |
| 12122 | |
| 12123 | - (WKNumberPadInputMode)numericInputModeForListViewController:(WKTextInputListViewController *)controller |
| 12124 | { |
| 12125 | switch (_focusedElementInformation.elementType) { |
| 12126 | case WebKit::InputType::Phone: |
| 12127 | return WKNumberPadInputModeTelephone; |
| 12128 | case WebKit::InputType::Number: |
| 12129 | return WKNumberPadInputModeNumbersAndSymbols; |
| 12130 | case WebKit::InputType::NumberPad: |
| 12131 | return WKNumberPadInputModeNumbersOnly; |
| 12132 | default: |
| 12133 | return WKNumberPadInputModeNone; |
| 12134 | } |
| 12135 | } |
| 12136 | |
| 12137 | - (PUICTextInputContext *)textInputContextForListViewController:(WKTextInputListViewController *)controller |
| 12138 | { |
| 12139 | return self.createQuickboardTextInputContext.autorelease(); |
| 12140 | } |
| 12141 | |
| 12142 | - (BOOL)allowsDictationInputForListViewController:(PUICQuickboardViewController *)controller |
| 12143 | { |
| 12144 | return _focusedElementInformation.elementType != WebKit::InputType::Password; |
| 12145 | } |
| 12146 | |
| 12147 | #endif // HAVE(PEPPER_UI_CORE) |
| 12148 | |
| 12149 | #if HAVE(LOOKUP_GESTURE_RECOGNIZER)(defined HAVE_LOOKUP_GESTURE_RECOGNIZER && HAVE_LOOKUP_GESTURE_RECOGNIZER ) |
| 12150 | - (void)_lookupGestureRecognized:(UIGestureRecognizer *)gestureRecognizer |
| 12151 | { |
| 12152 | NSPoint locationInViewCoordinates = [gestureRecognizer locationInView:self]; |
| 12153 | _page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates)); |
| 12154 | } |
| 12155 | #endif |
| 12156 | |
| 12157 | - (void)buildMenuForWebViewWithBuilder:(id <UIMenuBuilder>)builder |
| 12158 | { |
| 12159 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 12160 | if (auto menu = self.removeBackgroundMenu) |
| 12161 | [builder insertSiblingMenu:menu beforeMenuForIdentifier:UIMenuFormat]; |
| 12162 | #endif |
| 12163 | |
| 12164 | #if ENABLE(APP_HIGHLIGHTS)(defined 1 && 1) |
| 12165 | if (auto menu = self.appHighlightMenu) |
| 12166 | [builder insertChildMenu:menu atEndOfMenuForIdentifier:UIMenuRoot]; |
| 12167 | #endif |
| 12168 | |
| 12169 | if (auto menu = self.scrollToTextFragmentGenerationMenu) |
| 12170 | [builder insertSiblingMenu:menu beforeMenuForIdentifier:UIMenuShare]; |
| 12171 | } |
| 12172 | |
| 12173 | - (UIMenu *)menuWithInlineAction:(NSString *)title image:(UIImage *)image identifier:(NSString *)identifier handler:(Function<void(WKContentView *)>&&)handler |
| 12174 | { |
| 12175 | RetainPtr action = [UIAction actionWithTitle:title image:image identifier:identifier handler:makeBlockPtr([handler = WTF::move(handler), weakSelf = WeakObjCPtr<WKContentView>(self)](UIAction *) mutable { |
| 12176 | if (auto strongSelf = weakSelf.get()) |
| 12177 | handler(strongSelf.get()); |
| 12178 | }).get()]; |
| 12179 | return [UIMenu menuWithTitle:@"" image:nilnullptr identifier:nilnullptr options:UIMenuOptionsDisplayInline children:@[ action.get() ]]; |
| 12180 | } |
| 12181 | |
| 12182 | #if ENABLE(APP_HIGHLIGHTS)(defined 1 && 1) |
| 12183 | |
| 12184 | - (UIMenu *)appHighlightMenu |
| 12185 | { |
| 12186 | Ref page = *_page; |
| 12187 | if (!protect(page->preferences())->appHighlightsEnabled() || page->editorState().selectionType != WebCore::SelectionType::Range || !self.shouldAllowHighlightLinkCreation) |
| 12188 | return nilnullptr; |
| 12189 | |
| 12190 | bool isVisible = page->appHighlightsVisibility(); |
| 12191 | auto title = isVisible ? WebCore::contextMenuItemTagAddHighlightToCurrentQuickNote() : WebCore::contextMenuItemTagAddHighlightToNewQuickNote(); |
| 12192 | return [self menuWithInlineAction:title.createNSString().get() image:[UIImage _systemImageNamed:@"quicknote"] identifier:@"WKActionCreateQuickNote" handler:[isVisible](WKContentView *view) mutable { |
| 12193 | protect(view->_page)->createAppHighlightInSelectedRange(isVisible ? WebCore::CreateNewGroupForHighlight::No : WebCore::CreateNewGroupForHighlight::Yes, WebCore::HighlightRequestOriginatedInApp::No); |
| 12194 | }]; |
| 12195 | } |
| 12196 | |
| 12197 | #endif // ENABLE(APP_HIGHLIGHTS) |
| 12198 | |
| 12199 | - (UIMenu *)scrollToTextFragmentGenerationMenu |
| 12200 | { |
| 12201 | if (!protect(_page->preferences())->scrollToTextFragmentGenerationEnabled() || !self.shouldAllowHighlightLinkCreation) |
| 12202 | return nilnullptr; |
| 12203 | |
| 12204 | return [self menuWithInlineAction:WebCore::contextMenuItemTagCopyLinkWithHighlight().createNSString().get() image:[UIImage systemImageNamed:@"text.quote"] identifier:@"WKActionScrollToTextFragmentGeneration" handler:[](WKContentView *view) mutable { |
| 12205 | protect(view->_page)->copyLinkWithHighlight(); |
| 12206 | }]; |
| 12207 | } |
| 12208 | |
| 12209 | - (void)setContinuousSpellCheckingEnabled:(BOOL)enabled |
| 12210 | { |
| 12211 | if (WebKit::TextChecker::setContinuousSpellCheckingEnabled(enabled)) |
| 12212 | protect(_page->legacyMainFrameProcess())->updateTextCheckerState(); |
| 12213 | } |
| 12214 | |
| 12215 | - (void)setGrammarCheckingEnabled:(BOOL)enabled |
| 12216 | { |
| 12217 | if (static_cast<bool>(enabled) == WebKit::TextChecker::state().contains(WebKit::TextCheckerState::GrammarCheckingEnabled)) |
| 12218 | return; |
| 12219 | |
| 12220 | WebKit::TextChecker::setGrammarCheckingEnabled(enabled); |
| 12221 | protect(_page->legacyMainFrameProcess())->updateTextCheckerState(); |
| 12222 | } |
| 12223 | |
| 12224 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 12225 | |
| 12226 | - (BOOL)shouldUseMouseGestureRecognizer |
| 12227 | { |
| 12228 | static const BOOL shouldUseMouseGestureRecognizer = []() -> BOOL { |
| 12229 | // <rdar://problem/59521967> iAd Video does not respond to mouse events, only touch events |
| 12230 | if (WTF::IOSApplication::isNews() || WTF::IOSApplication::isStocks()) |
| 12231 | return NO__objc_no; |
| 12232 | |
| 12233 | if (!linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::SendsNativeMouseEvents) |
| 12234 | && WTF::IOSApplication::isEssentialSkeleton()) { // <rdar://problem/62694519> |
| 12235 | WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGINclang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" |
| 12236 | os_log_error(OS_LOG_DEFAULT, "WARNING: This application has been observed to ignore mouse events in web content; touch events will be sent until it is built against the iOS 13.4 SDK, but after that, the web content must respect mouse or pointer events in addition to touch events in order to behave correctly when a trackpad or mouse is used.")__extension__({ os_log_t _log_tmp = ((( os_log_t)&(_os_log_default ))); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled (_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p("WARNING: This application has been observed to ignore mouse events in web content; touch events will be sent until it is built against the iOS 13.4 SDK, but after that, the web content must respect mouse or pointer events in addition to touch events in order to behave correctly when a trackpad or mouse is used." ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG261" ) = "WARNING: This application has been observed to ignore mouse events in web content; touch events will be sent until it is built against the iOS 13.4 SDK, but after that, the web content must respect mouse or pointer events in addition to touch events in order to behave correctly when a trackpad or mouse is used." ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("WARNING: This application has been observed to ignore mouse events in web content; touch events will be sent until it is built against the iOS 13.4 SDK, but after that, the web content must respect mouse or pointer events in addition to touch events in order to behave correctly when a trackpad or mouse is used." )]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "WARNING: This application has been observed to ignore mouse events in web content; touch events will be sent until it is built against the iOS 13.4 SDK, but after that, the web content must respect mouse or pointer events in addition to touch events in order to behave correctly when a trackpad or mouse is used." ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }); |
| 12237 | WTF_ALLOW_UNSAFE_BUFFER_USAGE_ENDclang diagnostic pop |
| 12238 | return NO__objc_no; |
| 12239 | } |
| 12240 | |
| 12241 | return YES__objc_yes; |
| 12242 | }(); |
| 12243 | |
| 12244 | switch (_mouseEventPolicy) { |
| 12245 | case WebCore::MouseEventPolicy::Default: |
| 12246 | break; |
| 12247 | #if ENABLE(IOS_TOUCH_EVENTS)(defined 0 && 0) |
| 12248 | case WebCore::MouseEventPolicy::SynthesizeTouchEvents: |
| 12249 | return NO__objc_no; |
| 12250 | #endif |
| 12251 | } |
| 12252 | |
| 12253 | return shouldUseMouseGestureRecognizer; |
| 12254 | } |
| 12255 | |
| 12256 | - (void)setUpMouseGestureRecognizer |
| 12257 | { |
| 12258 | if (_mouseInteraction) |
| 12259 | [self removeInteraction:_mouseInteraction.get()]; |
| 12260 | |
| 12261 | _mouseInteraction = adoptNS([[WKMouseInteraction alloc] initWithDelegate:self]); |
| 12262 | [self addInteraction:_mouseInteraction.get()]; |
| 12263 | |
| 12264 | [self _configureMouseGestureRecognizer]; |
| 12265 | } |
| 12266 | |
| 12267 | - (void)mouseInteraction:(WKMouseInteraction *)interaction changedWithEvent:(const WebKit::NativeWebMouseEvent&)event |
| 12268 | { |
| 12269 | Ref page = *_page; |
| 12270 | if (!page->hasRunningProcess()) |
| 12271 | return; |
| 12272 | |
| 12273 | if (event.type() == WebKit::WebEventType::MouseDown) { |
| 12274 | _layerTreeTransactionIdAtLastInteractionStart = protect(downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*page->drawingArea()))->lastCommittedMainFrameLayerTreeTransactionID(); |
| 12275 | |
| 12276 | if (auto lastLocation = interaction.lastLocation) |
| 12277 | _lastInteractionLocation = *lastLocation; |
| 12278 | } else if (event.type() == WebKit::WebEventType::MouseUp) { |
| 12279 | _usingMouseDragForSelection = NO__objc_no; |
| 12280 | |
| 12281 | if (self.hasHiddenContentEditable && self._hasFocusedElement && !self.window.keyWindow) |
| 12282 | [self.window makeKeyWindow]; |
| 12283 | } |
| 12284 | |
| 12285 | page->handleMouseEvent(event); |
| 12286 | } |
| 12287 | |
| 12288 | #if ENABLE(POINTER_LOCK)(defined 1 && 1) |
| 12289 | - (void)mouseInteractionDidLoseMouseDeviceDuringPointerLock:(WKMouseInteraction *)interaction |
| 12290 | { |
| 12291 | Ref page = *_page; |
| 12292 | if (!page->hasRunningProcess()) |
| 12293 | return; |
| 12294 | |
| 12295 | page->resetPointerLockState(); |
| 12296 | } |
| 12297 | #endif |
| 12298 | |
| 12299 | - (void)_configureMouseGestureRecognizer |
| 12300 | { |
| 12301 | [_mouseInteraction setEnabled:self.shouldUseMouseGestureRecognizer]; |
| 12302 | } |
| 12303 | |
| 12304 | - (void)_setMouseEventPolicy:(WebCore::MouseEventPolicy)policy |
| 12305 | { |
| 12306 | _mouseEventPolicy = policy; |
| 12307 | [self _configureMouseGestureRecognizer]; |
| 12308 | } |
| 12309 | |
| 12310 | #endif // HAVE(UIKIT_WITH_MOUSE_SUPPORT) |
| 12311 | |
| 12312 | #if ENABLE(MEDIA_CONTROLS_CONTEXT_MENUS)(defined 1 && 1) && USE(UICONTEXTMENU)(defined 1 && 1) |
| 12313 | |
| 12314 | - (void)_showMediaControlsContextMenu:(WebCore::FloatRect&&)targetFrame items:(Vector<WebCore::MediaControlsContextMenuItem>&&)items frameInfo:(const WebKit::FrameInfoData&)frameInfo identifier:(WebCore::HTMLMediaElementIdentifier)identifier completionHandler:(CompletionHandler<void(WebCore::MediaControlsContextMenuItem::ID)>&&)completionHandler |
| 12315 | { |
| 12316 | [protect(_actionSheetAssistant) showMediaControlsContextMenu:WTF::move(targetFrame) items:WTF::move(items) frameInfo:frameInfo identifier:identifier completionHandler:WTF::move(completionHandler)]; |
| 12317 | } |
| 12318 | |
| 12319 | - (void)captionStyleMenuWillOpenWithFrameInfo:(const WebKit::FrameInfoData&)frameInfo identifier:(WebCore::HTMLMediaElementIdentifier)identifier |
| 12320 | { |
| 12321 | protect(_page)->showCaptionDisplaySettingsPreview(frameInfo, identifier); |
| 12322 | } |
| 12323 | |
| 12324 | - (void)captionStyleMenuDidCloseWithFrameInfo:(const WebKit::FrameInfoData&)frameInfo identifier:(WebCore::HTMLMediaElementIdentifier)identifier |
| 12325 | { |
| 12326 | protect(_page)->hideCaptionDisplaySettingsPreview(frameInfo, identifier); |
| 12327 | } |
| 12328 | #endif // ENABLE(MEDIA_CONTROLS_CONTEXT_MENUS) && USE(UICONTEXTMENU) |
| 12329 | |
| 12330 | #if ENABLE(VIDEO)(defined 1 && 1) && USE(UICONTEXTMENU)(defined 1 && 1) |
| 12331 | - (void)showCaptionDisplaySettingsMenu:(WebCore::HTMLMediaElementIdentifier)identifier withOptions:(const WebCore::ResolvedCaptionDisplaySettingsOptions&)options completionHandler:(CompletionHandler<void(Expected<void, WebCore::ExceptionData>)>&&)completionHandler |
| 12332 | { |
| 12333 | [protect(_actionSheetAssistant) showCaptionDisplaySettingsMenu:identifier withOptions:options completionHandler:WTF::move(completionHandler)]; |
| 12334 | } |
| 12335 | #endif |
| 12336 | |
| 12337 | #if HAVE(UI_POINTER_INTERACTION)(defined 1 && 1) |
| 12338 | |
| 12339 | - (void)setUpPointerInteraction |
| 12340 | { |
| 12341 | _pointerInteraction = adoptNS([[UIPointerInteraction alloc] initWithDelegate:self]); |
| 12342 | [self addInteraction:_pointerInteraction.get()]; |
| 12343 | } |
| 12344 | |
| 12345 | - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion |
| 12346 | { |
| 12347 | [self _updateLastPointerRegionIfNeeded:request]; |
| 12348 | return _lastPointerRegion.get(); |
| 12349 | } |
| 12350 | |
| 12351 | - (void)_updateLastPointerRegionIfNeeded:(UIPointerRegionRequest *)request |
| 12352 | { |
| 12353 | WebKit::InteractionInformationRequest interactionInformationRequest; |
| 12354 | interactionInformationRequest.point = WebCore::roundedIntPoint(request.location); |
| 12355 | interactionInformationRequest.includeCursorContext = true; |
| 12356 | interactionInformationRequest.includeHasDoubleClickHandler = false; |
| 12357 | |
| 12358 | if ([self _currentPositionInformationIsValidForRequest:interactionInformationRequest]) { |
| 12359 | _lastPointerRegion = [self pointerRegionForPositionInformation:_positionInformation point:request.location]; |
| 12360 | [_webView.get() _setPointerTouchCompatibilitySimulatorEnabled:_positionInformation.needsPointerTouchCompatibilityQuirk]; |
| 12361 | _pointerInteractionRegionNeedsUpdate = NO__objc_no; |
| 12362 | return; |
| 12363 | } |
| 12364 | |
| 12365 | // Note: In this case where position information is not up-to-date, checking the last cached |
| 12366 | // position information may not yield expected results, since the client (or any other code in |
| 12367 | // WebKit) may have queried position information at a totally different location, for unrelated |
| 12368 | // reasons. However, this seems to work well in practice, since pointer updates are very frequent, |
| 12369 | // so any invalid state would only persist for a single frame. |
| 12370 | if (self.webView._editable && !_positionInformation.cursorContext.shouldNotUseIBeamInEditableContent) { |
| 12371 | auto lastRegionRect = _lastPointerRegion ? [_lastPointerRegion rect] : self.bounds; |
| 12372 | _lastPointerRegion = [UIPointerRegion regionWithRect:lastRegionRect identifier:editablePointerRegionIdentifier]; |
| 12373 | } |
| 12374 | |
| 12375 | if (_pointerInteractionRegionNeedsUpdate) |
| 12376 | return; |
| 12377 | |
| 12378 | _pointerInteractionRegionNeedsUpdate = YES__objc_yes; |
| 12379 | |
| 12380 | [self doAfterPositionInformationUpdate:[weakSelf = WeakObjCPtr<WKContentView>(self), location = request.location](WebKit::InteractionInformationAtPosition information) { |
| 12381 | auto strongSelf = weakSelf.get(); |
| 12382 | if (!strongSelf) |
| 12383 | return; |
| 12384 | |
| 12385 | strongSelf->_pointerInteractionRegionNeedsUpdate = NO__objc_no; |
| 12386 | strongSelf->_lastPointerRegion = [strongSelf pointerRegionForPositionInformation:information point:location]; |
| 12387 | [strongSelf->_webView.get() _setPointerTouchCompatibilitySimulatorEnabled:information.needsPointerTouchCompatibilityQuirk]; |
| 12388 | [strongSelf->_pointerInteraction invalidate]; |
| 12389 | } forRequest:interactionInformationRequest]; |
| 12390 | } |
| 12391 | |
| 12392 | - (UIPointerRegion *)pointerRegionForPositionInformation:(const WebKit::InteractionInformationAtPosition&)interactionInformation point:(CGPoint)location |
| 12393 | { |
| 12394 | WebCore::FloatRect expandedLineRect = enclosingIntRect(interactionInformation.cursorContext.lineCaretExtent); |
| 12395 | |
| 12396 | // Pad lines of text in order to avoid switching back to the dot cursor between lines. |
| 12397 | // This matches the value that UIKit uses. |
| 12398 | // FIXME: Should this account for vertical writing mode? |
| 12399 | expandedLineRect.inflateY(10); |
| 12400 | |
| 12401 | if (auto cursor = interactionInformation.cursorContext.cursor) { |
| 12402 | auto cursorType = cursor->type(); |
| 12403 | if (cursorType == WebCore::Cursor::Type::Hand) |
| 12404 | return [UIPointerRegion regionWithRect:interactionInformation.bounds identifier:pointerRegionIdentifier]; |
| 12405 | |
| 12406 | if (cursorType == WebCore::Cursor::Type::IBeam && expandedLineRect.contains(location)) |
| 12407 | return [UIPointerRegion regionWithRect:expandedLineRect identifier:pointerRegionIdentifier]; |
| 12408 | } |
| 12409 | |
| 12410 | if (self.webView._editable) { |
| 12411 | if (expandedLineRect.contains(location)) |
| 12412 | return [UIPointerRegion regionWithRect:expandedLineRect identifier:pointerRegionIdentifier]; |
| 12413 | return [UIPointerRegion regionWithRect:self.bounds identifier:editablePointerRegionIdentifier]; |
| 12414 | } |
| 12415 | |
| 12416 | return [UIPointerRegion regionWithRect:self.bounds identifier:pointerRegionIdentifier]; |
| 12417 | } |
| 12418 | |
| 12419 | - (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region |
| 12420 | { |
| 12421 | double scaleFactor = self._contentZoomScale; |
| 12422 | |
| 12423 | auto lineCaretExtent = _positionInformation.cursorContext.lineCaretExtent; |
| 12424 | UIPointerStyle *(^iBeamCursor)(void) = ^{ |
| 12425 | bool isVertical = _positionInformation.cursorContext.isVerticalWritingMode; |
| 12426 | float beamLength = (isVertical ? lineCaretExtent.width() : lineCaretExtent.height()) * scaleFactor; |
| 12427 | auto axisOrientation = isVertical ? UIAxisHorizontal : UIAxisVertical; |
| 12428 | auto iBeamConstraintAxes = isVertical ? UIAxisHorizontal : UIAxisVertical; |
| 12429 | auto regionLengthInBlockAxis = isVertical ? CGRectGetWidth(region.rect) : CGRectGetHeight(region.rect); |
| 12430 | |
| 12431 | // If the I-beam is so large that the magnetism is hard to fight, we should not apply any magnetism. |
| 12432 | static constexpr auto maximumBeamSnappingLength = 100; |
| 12433 | if (beamLength > maximumBeamSnappingLength || regionLengthInBlockAxis > maximumBeamSnappingLength) |
| 12434 | iBeamConstraintAxes = UIAxisNeither; |
| 12435 | |
| 12436 | // If the region is the size of the view, we should not apply any magnetism. |
| 12437 | if ([region.identifier isEqual:editablePointerRegionIdentifier]) |
| 12438 | iBeamConstraintAxes = UIAxisNeither; |
| 12439 | |
| 12440 | return [UIPointerStyle styleWithShape:[UIPointerShape beamWithPreferredLength:beamLength axis:axisOrientation] constrainedAxes:iBeamConstraintAxes]; |
| 12441 | }; |
| 12442 | |
| 12443 | if (self.webView._editable) { |
| 12444 | if (_positionInformation.cursorContext.shouldNotUseIBeamInEditableContent) |
| 12445 | return [UIPointerStyle systemPointerStyle]; |
| 12446 | return iBeamCursor(); |
| 12447 | } |
| 12448 | |
| 12449 | if (auto cursor = _positionInformation.cursorContext.cursor; cursor && [region.identifier isEqual:pointerRegionIdentifier]) { |
| 12450 | auto cursorType = cursor->type(); |
| 12451 | if (cursorType == WebCore::Cursor::Type::Hand) |
| 12452 | return [UIPointerStyle systemPointerStyle]; |
| 12453 | |
| 12454 | if (cursorType == WebCore::Cursor::Type::IBeam && lineCaretExtent.contains(_positionInformation.request.point)) |
| 12455 | return iBeamCursor(); |
| 12456 | } |
| 12457 | |
| 12458 | return [UIPointerStyle systemPointerStyle]; |
| 12459 | } |
| 12460 | |
| 12461 | #endif // HAVE(UI_POINTER_INTERACTION) |
| 12462 | |
| 12463 | #if HAVE(PENCILKIT_TEXT_INPUT)(defined 1 && 1) |
| 12464 | |
| 12465 | - (void)setUpScribbleInteraction |
| 12466 | { |
| 12467 | _scribbleInteraction = adoptNS([[UIIndirectScribbleInteraction alloc] initWithDelegate:self]); |
| 12468 | [self addInteraction:_scribbleInteraction.get()]; |
| 12469 | } |
| 12470 | |
| 12471 | - (void)cleanUpScribbleInteraction |
| 12472 | { |
| 12473 | [self removeInteraction:_scribbleInteraction.get()]; |
| 12474 | _scribbleInteraction = nilnullptr; |
| 12475 | } |
| 12476 | |
| 12477 | - (_WKTextInputContext *)_textInputContextByScribbleIdentifier:(UIScribbleElementIdentifier)identifier |
| 12478 | { |
| 12479 | _WKTextInputContext *textInputContext = (_WKTextInputContext *)identifier; |
| 12480 | if (![textInputContext isKindOfClass:_WKTextInputContext.class]) |
| 12481 | return nilnullptr; |
| 12482 | auto elementContext = textInputContext._textInputContext; |
| 12483 | if (elementContext.webPageIdentifier != _page->webPageIDInMainFrameProcess()) |
| 12484 | return nilnullptr; |
| 12485 | return textInputContext; |
| 12486 | } |
| 12487 | |
| 12488 | - (BOOL)_elementForTextInputContextIsFocused:(_WKTextInputContext *)context |
| 12489 | { |
| 12490 | // We ignore bounding rect changes as the bounding rect of the focused element is not kept up-to-date. |
| 12491 | return context && [self _isSameAsFocusedElement:context._textInputContext]; |
| 12492 | } |
| 12493 | |
| 12494 | - (void)indirectScribbleInteraction:(UIIndirectScribbleInteraction *)interaction requestElementsInRect:(CGRect)rect completion:(void(^)(NSArray<UIScribbleElementIdentifier> *))completion |
| 12495 | { |
| 12496 | ASSERT(_scribbleInteraction.get() == interaction)((void)0); |
| 12497 | [self _requestTextInputContextsInRect:rect completionHandler:completion]; |
| 12498 | } |
| 12499 | |
| 12500 | - (BOOL)indirectScribbleInteraction:(UIIndirectScribbleInteraction *)interaction isElementFocused:(UIScribbleElementIdentifier)identifier |
| 12501 | { |
| 12502 | ASSERT(_scribbleInteraction.get() == interaction)((void)0); |
| 12503 | return [self _elementForTextInputContextIsFocused:[self _textInputContextByScribbleIdentifier:identifier]]; |
| 12504 | } |
| 12505 | |
| 12506 | - (void)indirectScribbleInteraction:(UIIndirectScribbleInteraction *)interaction focusElementIfNeeded:(UIScribbleElementIdentifier)identifier referencePoint:(CGPoint)initialPoint completion:(void (^)(UIResponder<UITextInput> *))completionBlock |
| 12507 | { |
| 12508 | ASSERT(_scribbleInteraction.get() == interaction)((void)0); |
| 12509 | if (auto *textInputContext = [self _textInputContextByScribbleIdentifier:identifier]) |
| 12510 | [self _focusTextInputContext:textInputContext placeCaretAt:initialPoint completionHandler:completionBlock]; |
| 12511 | else |
| 12512 | completionBlock(nilnullptr); |
| 12513 | } |
| 12514 | |
| 12515 | - (CGRect)indirectScribbleInteraction:(UIIndirectScribbleInteraction *)interaction frameForElement:(UIScribbleElementIdentifier)identifier |
| 12516 | { |
| 12517 | ASSERT(_scribbleInteraction.get() == interaction)((void)0); |
| 12518 | auto *textInputContext = [self _textInputContextByScribbleIdentifier:identifier]; |
| 12519 | return textInputContext ? textInputContext.boundingRect : CGRectNull; |
| 12520 | } |
| 12521 | |
| 12522 | - (void)indirectScribbleInteraction:(UIIndirectScribbleInteraction *)interaction willBeginWritingInElement:(UIScribbleElementIdentifier)identifier |
| 12523 | { |
| 12524 | ASSERT(_scribbleInteraction.get() == interaction)((void)0); |
| 12525 | if (auto *textInputContext = [self _textInputContextByScribbleIdentifier:identifier]) |
| 12526 | [self _willBeginTextInteractionInTextInputContext:textInputContext]; |
| 12527 | } |
| 12528 | |
| 12529 | - (void)indirectScribbleInteraction:(UIIndirectScribbleInteraction *)interaction didFinishWritingInElement:(UIScribbleElementIdentifier)identifier |
| 12530 | { |
| 12531 | ASSERT(_scribbleInteraction.get() == interaction)((void)0); |
| 12532 | if (auto *textInputContext = [self _textInputContextByScribbleIdentifier:identifier]) |
| 12533 | [self _didFinishTextInteractionInTextInputContext:textInputContext]; |
| 12534 | } |
| 12535 | |
| 12536 | #endif // HAVE(PENCILKIT_TEXT_INPUT) |
| 12537 | |
| 12538 | #if ENABLE(ATTACHMENT_ELEMENT)(defined 1 && 1) |
| 12539 | |
| 12540 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) && !PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV ) |
| 12541 | |
| 12542 | static RetainPtr<NSItemProvider> createItemProvider(const WebKit::WebPageProxy& page, const WebCore::PromisedAttachmentInfo& info) |
| 12543 | { |
| 12544 | |
| 12545 | auto attachment = page.attachmentForIdentifier(info.attachmentIdentifier); |
| 12546 | if (!attachment) |
| 12547 | return { }; |
| 12548 | |
| 12549 | RetainPtr utiType = attachment->utiType().createNSString(); |
| 12550 | if (![utiType length]) |
| 12551 | return { }; |
| 12552 | |
| 12553 | if (attachment->isEmpty()) |
| 12554 | return { }; |
| 12555 | |
| 12556 | auto item = adoptNS([[NSItemProvider alloc] init]); |
| 12557 | [item setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment]; |
| 12558 | |
| 12559 | RetainPtr fileName = attachment->fileName().createNSString(); |
| 12560 | if ([fileName length]) |
| 12561 | [item setSuggestedName:fileName.get()]; |
| 12562 | |
| 12563 | for (size_t index = 0; index < info.additionalTypesAndData.size(); ++index) { |
| 12564 | auto nsData = protect(info.additionalTypesAndData[index].second)->createNSData(); |
| 12565 | [item registerDataRepresentationForTypeIdentifier:info.additionalTypesAndData[index].first.createNSString().get() visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[nsData](void (^completionHandler)(NSData *, NSError *)) -> NSProgress * { |
| 12566 | completionHandler(nsData.get(), nilnullptr); |
| 12567 | return nilnullptr; |
| 12568 | }]; |
| 12569 | } |
| 12570 | |
| 12571 | [item registerDataRepresentationForTypeIdentifier:utiType.get() visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[attachment](void (^completionHandler)(NSData *, NSError *)) -> NSProgress * { |
| 12572 | attachment->doWithFileWrapper([&](NSFileWrapper *fileWrapper) { |
| 12573 | if (auto nsData = RetainPtr { fileWrapper.regularFileContents }) |
| 12574 | completionHandler(nsData.get(), nilnullptr); |
| 12575 | else |
| 12576 | completionHandler(nilnullptr, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nilnullptr]); |
| 12577 | }); |
| 12578 | return nilnullptr; |
| 12579 | }]; |
| 12580 | |
| 12581 | return item; |
| 12582 | } |
| 12583 | |
| 12584 | #endif // !PLATFORM(WATCHOS) && !PLATFORM(APPLETV) |
| 12585 | |
| 12586 | - (void)_writePromisedAttachmentToPasteboard:(WebCore::PromisedAttachmentInfo&&)info |
| 12587 | { |
| 12588 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) && !PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV ) |
| 12589 | auto item = createItemProvider(*protect(_page), WTF::move(info)); |
| 12590 | if (!item) |
| 12591 | return; |
| 12592 | |
| 12593 | [UIPasteboard _performAsDataOwner:[_webView.get() _effectiveDataOwner:self._dataOwnerForCopy] block:^{ |
| 12594 | UIPasteboard.generalPasteboard.itemProviders = @[ item.get() ]; |
| 12595 | }]; |
| 12596 | #else |
| 12597 | UNUSED_PARAM(info)(void)info; |
| 12598 | #endif |
| 12599 | } |
| 12600 | |
| 12601 | #endif // ENABLE(ATTACHMENT_ELEMENT) |
| 12602 | |
| 12603 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 12604 | |
| 12605 | - (void)_endImageAnalysisGestureDeferral:(WebKit::ShouldPreventGestures)shouldPreventGestures |
| 12606 | { |
| 12607 | [_imageAnalysisDeferringGestureRecognizer endDeferral:shouldPreventGestures]; |
| 12608 | } |
| 12609 | |
| 12610 | - (void)_doAfterPendingImageAnalysis:(void(^)(WebKit::ProceedWithTextSelectionInImage))block |
| 12611 | { |
| 12612 | if (self.hasPendingImageAnalysisRequest) |
| 12613 | _actionsToPerformAfterPendingImageAnalysis.append(makeBlockPtr(block)); |
| 12614 | else |
| 12615 | block(WebKit::ProceedWithTextSelectionInImage::No); |
| 12616 | } |
| 12617 | |
| 12618 | - (void)_invokeAllActionsToPerformAfterPendingImageAnalysis:(WebKit::ProceedWithTextSelectionInImage)proceedWithTextSelectionInImage |
| 12619 | { |
| 12620 | _pendingImageAnalysisRequestIdentifier = std::nullopt; |
| 12621 | _elementPendingImageAnalysis = std::nullopt; |
| 12622 | for (auto block : std::exchange(_actionsToPerformAfterPendingImageAnalysis, { })) |
| 12623 | block(proceedWithTextSelectionInImage); |
| 12624 | } |
| 12625 | |
| 12626 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 12627 | |
| 12628 | - (CALayer *)textIndicatorInstallationLayer |
| 12629 | { |
| 12630 | return [self layer]; |
| 12631 | } |
| 12632 | |
| 12633 | - (void)setUpTextIndicator:(Ref<WebCore::TextIndicator>)textIndicator |
| 12634 | { |
| 12635 | if (_textIndicator == textIndicator.ptr()) |
| 12636 | return; |
| 12637 | |
| 12638 | [self teardownTextIndicatorLayer]; |
| 12639 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startFadeOut) object:nilnullptr]; |
| 12640 | |
| 12641 | _textIndicator = textIndicator.ptr(); |
| 12642 | |
| 12643 | CGRect frame = _textIndicator->textBoundingRectInRootViewCoordinates(); |
| 12644 | _textIndicatorLayer = adoptNS([[WebTextIndicatorLayer alloc] initWithFrame:frame |
| 12645 | textIndicator:textIndicator.ptr() margin:CGSizeZero offset:CGPointZero]); |
| 12646 | |
| 12647 | [[self layer] addSublayer:_textIndicatorLayer.get()]; |
| 12648 | |
| 12649 | if (_textIndicator->presentationTransition() != WebCore::TextIndicatorPresentationTransition::None) |
| 12650 | [_textIndicatorLayer present]; |
| 12651 | |
| 12652 | [self performSelector:@selector(startFadeOut) withObject:self afterDelay:WebCore::timeBeforeFadeStarts.value()]; |
| 12653 | } |
| 12654 | |
| 12655 | - (void)updateTextIndicator:(Ref<WebCore::TextIndicator>)textIndicator |
| 12656 | { |
| 12657 | if (_textIndicator != textIndicator.ptr()) |
| 12658 | return; |
| 12659 | |
| 12660 | CGRect frame = _textIndicator->textBoundingRectInRootViewCoordinates(); |
| 12661 | _textIndicatorLayer = adoptNS([[WebTextIndicatorLayer alloc] initWithFrame:frame |
| 12662 | textIndicator:textIndicator.ptr() margin:CGSizeZero offset:CGPointZero]); |
| 12663 | |
| 12664 | [[self layer] addSublayer:_textIndicatorLayer.get()]; |
| 12665 | } |
| 12666 | |
| 12667 | - (void)clearTextIndicator:(WebCore::TextIndicatorDismissalAnimation)animation |
| 12668 | { |
| 12669 | RefPtr<WebCore::TextIndicator> textIndicator = WTF::move(_textIndicator); |
| 12670 | |
| 12671 | if ([_textIndicatorLayer isFadingOut]) |
| 12672 | return; |
| 12673 | |
| 12674 | if (textIndicator && textIndicator->wantsManualAnimation() && [_textIndicatorLayer hasCompletedAnimation] && animation == WebCore::TextIndicatorDismissalAnimation::FadeOut) { |
| 12675 | [self startFadeOut]; |
| 12676 | return; |
| 12677 | } |
| 12678 | |
| 12679 | [self teardownTextIndicatorLayer]; |
| 12680 | } |
| 12681 | |
| 12682 | - (void)setTextIndicatorAnimationProgress:(float)animationProgress |
| 12683 | { |
| 12684 | if (!_textIndicator) |
| 12685 | return; |
| 12686 | |
| 12687 | [_textIndicatorLayer setAnimationProgress:animationProgress]; |
| 12688 | } |
| 12689 | |
| 12690 | - (void)teardownTextIndicatorLayer |
| 12691 | { |
| 12692 | [_textIndicatorLayer removeFromSuperlayer]; |
| 12693 | _textIndicatorLayer = nilnullptr; |
| 12694 | } |
| 12695 | |
| 12696 | - (void)startFadeOut |
| 12697 | { |
| 12698 | [_textIndicatorLayer setFadingOut:YES__objc_yes]; |
| 12699 | |
| 12700 | [_textIndicatorLayer hideWithCompletionHandler:[weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 12701 | auto strongSelf = weakSelf.get(); |
| 12702 | [strongSelf teardownTextIndicatorLayer]; |
| 12703 | }]; |
| 12704 | } |
| 12705 | |
| 12706 | #if HAVE(UIFINDINTERACTION)(defined 1 && 1) |
| 12707 | |
| 12708 | - (void)find:(id)sender |
| 12709 | { |
| 12710 | [self.webView find:sender]; |
| 12711 | } |
| 12712 | |
| 12713 | - (void)findAndReplace:(id)sender |
| 12714 | { |
| 12715 | [self.webView findAndReplace:sender]; |
| 12716 | } |
| 12717 | |
| 12718 | - (void)findNext:(id)sender |
| 12719 | { |
| 12720 | [self.webView findNext:sender]; |
| 12721 | } |
| 12722 | |
| 12723 | - (void)findPrevious:(id)sender |
| 12724 | { |
| 12725 | [self.webView findPrevious:sender]; |
| 12726 | } |
| 12727 | |
| 12728 | - (void)useSelectionForFindForWebView:(id)sender |
| 12729 | { |
| 12730 | if (!protect(_page)->hasSelectedRange()) |
| 12731 | return; |
| 12732 | |
| 12733 | NSString *selectedText = self.selectedText; |
| 12734 | if (selectedText.length) { |
| 12735 | [[self.webView findInteraction] setSearchText:selectedText]; |
| 12736 | UIFindInteraction._globalFindBuffer = selectedText; |
| 12737 | } |
| 12738 | } |
| 12739 | |
| 12740 | - (void)_findSelectedForWebView:(id)sender |
| 12741 | { |
| 12742 | ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED()do { if (self.shouldUseAsyncInteractions) [[unlikely]] { clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInteraction .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Received unexpected call to %s"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG262" ) = "Received unexpected call to %s"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Received unexpected call to %s", __PRETTY_FUNCTION__)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Received unexpected call to %s" , __PRETTY_FUNCTION__), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; if (WebKit::isFullWebBrowserOrRunningTest ()) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(12742, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm" , __PRETTY_FUNCTION__ ); } while (false); else ((void)0); } } while (0); |
| 12743 | |
| 12744 | [self findSelectedForWebView:sender]; |
| 12745 | } |
| 12746 | |
| 12747 | - (void)findSelectedForWebView:(id)sender |
| 12748 | { |
| 12749 | [self useSelectionForFindForWebView:sender]; |
| 12750 | [self.webView find:sender]; |
| 12751 | } |
| 12752 | |
| 12753 | - (void)performTextSearchWithQueryString:(NSString *)string usingOptions:(UITextSearchOptions *)options resultAggregator:(id<UITextSearchAggregator>)aggregator |
| 12754 | { |
| 12755 | OptionSet<WebKit::FindOptions> findOptions; |
| 12756 | switch (options.wordMatchMethod) { |
| 12757 | case UITextSearchMatchMethodStartsWith: |
| 12758 | findOptions.add(WebKit::FindOptions::AtWordStarts); |
| 12759 | break; |
| 12760 | case UITextSearchMatchMethodFullWord: |
| 12761 | findOptions.add({ WebKit::FindOptions::AtWordStarts, WebKit::FindOptions::AtWordEnds }); |
| 12762 | break; |
| 12763 | default: |
| 12764 | break; |
| 12765 | } |
| 12766 | |
| 12767 | if (options.stringCompareOptions & NSCaseInsensitiveSearch) |
| 12768 | findOptions.add(WebKit::FindOptions::CaseInsensitive); |
| 12769 | |
| 12770 | // The limit matches the limit set on existing WKWebView find API. |
| 12771 | constexpr auto maxMatches = 1000; |
| 12772 | protect(_page)->findTextRangesForStringMatches(string, findOptions, maxMatches, [string = retainPtr(string), aggregator = retainPtr(aggregator)](const Vector<WebKit::WebFoundTextRange> ranges) { |
| 12773 | for (auto& range : ranges) { |
| 12774 | RetainPtr textRange = [WKFoundTextRange foundTextRangeWithWebFoundTextRange:range]; |
| 12775 | [aggregator foundRange:textRange.get() forSearchString:string.get() inDocument:nilnullptr]; |
| 12776 | } |
| 12777 | |
| 12778 | [aggregator finishedSearching]; |
| 12779 | }); |
| 12780 | } |
| 12781 | |
| 12782 | - (void)replaceFoundTextInRange:(UITextRange *)range inDocument:(UITextSearchDocumentIdentifier)document withText:(NSString *)replacementText |
| 12783 | { |
| 12784 | if (!self.supportsTextReplacement) |
| 12785 | return; |
| 12786 | |
| 12787 | auto foundTextRange = dynamic_objc_cast<WKFoundTextRange>(range); |
| 12788 | if (!foundTextRange) |
| 12789 | return; |
| 12790 | |
| 12791 | protect(_page)->replaceFoundTextRangeWithString([foundTextRange webFoundTextRange], replacementText); |
| 12792 | } |
| 12793 | |
| 12794 | - (void)decorateFoundTextRange:(UITextRange *)range inDocument:(UITextSearchDocumentIdentifier)document usingStyle:(UITextSearchFoundTextStyle)style |
| 12795 | { |
| 12796 | auto foundTextRange = dynamic_objc_cast<WKFoundTextRange>(range); |
| 12797 | if (!foundTextRange) |
| 12798 | return; |
| 12799 | |
| 12800 | auto decorationStyle = WebKit::FindDecorationStyle::Normal; |
| 12801 | if (style == UITextSearchFoundTextStyleFound) |
| 12802 | decorationStyle = WebKit::FindDecorationStyle::Found; |
| 12803 | else if (style == UITextSearchFoundTextStyleHighlighted) |
| 12804 | decorationStyle = WebKit::FindDecorationStyle::Highlighted; |
| 12805 | |
| 12806 | protect(_page)->decorateTextRangeWithStyle([foundTextRange webFoundTextRange], decorationStyle); |
| 12807 | } |
| 12808 | |
| 12809 | - (void)scrollRangeToVisible:(UITextRange *)range inDocument:(UITextSearchDocumentIdentifier)document |
| 12810 | { |
| 12811 | if (auto foundTextRange = dynamic_objc_cast<WKFoundTextRange>(range)) |
| 12812 | protect(_page)->scrollTextRangeToVisible([foundTextRange webFoundTextRange]); |
| 12813 | } |
| 12814 | |
| 12815 | - (void)clearAllDecoratedFoundText |
| 12816 | { |
| 12817 | protect(_page)->clearAllDecoratedFoundText(); |
| 12818 | } |
| 12819 | |
| 12820 | - (void)didBeginTextSearchOperation |
| 12821 | { |
| 12822 | [self.webView _showFindOverlay]; |
| 12823 | protect(_page)->didBeginTextSearchOperation(); |
| 12824 | } |
| 12825 | |
| 12826 | - (void)didEndTextSearchOperation |
| 12827 | { |
| 12828 | [self.webView _hideFindOverlay]; |
| 12829 | } |
| 12830 | |
| 12831 | - (BOOL)supportsTextReplacement |
| 12832 | { |
| 12833 | return self.webView.supportsTextReplacement; |
| 12834 | } |
| 12835 | |
| 12836 | - (BOOL)supportsTextReplacementForWebView |
| 12837 | { |
| 12838 | return self.webView._editable; |
| 12839 | } |
| 12840 | |
| 12841 | - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition inDocument:(UITextSearchDocumentIdentifier)document |
| 12842 | { |
| 12843 | return [self offsetFromPosition:from toPosition:toPosition]; |
| 12844 | } |
| 12845 | |
| 12846 | - (NSComparisonResult)compareFoundRange:(UITextRange *)fromRange toRange:(UITextRange *)toRange inDocument:(UITextSearchDocumentIdentifier)document |
| 12847 | { |
| 12848 | NSInteger offset = [self offsetFromPosition:fromRange.start toPosition:toRange.start]; |
| 12849 | |
| 12850 | if (offset < 0) |
| 12851 | return NSOrderedAscending; |
| 12852 | |
| 12853 | if (offset > 0) |
| 12854 | return NSOrderedDescending; |
| 12855 | |
| 12856 | return NSOrderedSame; |
| 12857 | } |
| 12858 | |
| 12859 | - (void)requestRectForFoundTextRange:(UITextRange *)range completionHandler:(void (^)(CGRect))completionHandler |
| 12860 | { |
| 12861 | if (auto* foundTextRange = dynamic_objc_cast<WKFoundTextRange>(range)) { |
| 12862 | protect(_page)->requestRectForFoundTextRange([foundTextRange webFoundTextRange], [completionHandler = makeBlockPtr(completionHandler)] (WebCore::FloatRect rect) { |
| 12863 | completionHandler(rect); |
| 12864 | }); |
| 12865 | return; |
| 12866 | } |
| 12867 | |
| 12868 | completionHandler(CGRectZero); |
| 12869 | } |
| 12870 | |
| 12871 | #endif // HAVE(UIFINDINTERACTION) |
| 12872 | |
| 12873 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 12874 | |
| 12875 | - (BOOL)hasSelectableTextForImageContextMenu |
| 12876 | { |
| 12877 | return valueOrDefault(_imageAnalysisContextMenuActionData).hasSelectableText; |
| 12878 | } |
| 12879 | |
| 12880 | - (BOOL)hasVisualSearchResultsForImageContextMenu |
| 12881 | { |
| 12882 | return valueOrDefault(_imageAnalysisContextMenuActionData).hasVisualSearchResults; |
| 12883 | } |
| 12884 | |
| 12885 | - (CGImageRef)subjectResultForImageContextMenu |
| 12886 | { |
| 12887 | return valueOrDefault(_imageAnalysisContextMenuActionData).copySubjectResult.getAutoreleased(); |
| 12888 | } |
| 12889 | |
| 12890 | - (UIMenu *)machineReadableCodeSubMenuForImageContextMenu |
| 12891 | { |
| 12892 | return valueOrDefault(_imageAnalysisContextMenuActionData).machineReadableCodeMenu.getAutoreleased(); |
| 12893 | } |
| 12894 | |
| 12895 | #if USE(QUICK_LOOK)(defined 1 && 1) |
| 12896 | |
| 12897 | - (void)presentVisualSearchPreviewControllerForImage:(UIImage *)image imageURL:(NSURL *)imageURL title:(NSString *)title imageBounds:(CGRect)imageBounds appearanceActions:(QLPreviewControllerFirstTimeAppearanceActions)appearanceActions |
| 12898 | { |
| 12899 | ASSERT(self.hasSelectableTextForImageContextMenu || self.hasVisualSearchResultsForImageContextMenu)((void)0); |
| 12900 | |
| 12901 | ASSERT(!_visualSearchPreviewController)((void)0); |
| 12902 | _visualSearchPreviewController = adoptNS([PAL::allocQLPreviewControllerInstance() init]); |
| 12903 | [_visualSearchPreviewController setDelegate:self]; |
| 12904 | [_visualSearchPreviewController setDataSource:self]; |
| 12905 | [_visualSearchPreviewController setAppearanceActions:appearanceActions]; |
| 12906 | [_visualSearchPreviewController setModalPresentationStyle:UIModalPresentationOverFullScreen]; |
| 12907 | |
| 12908 | ASSERT(!_visualSearchPreviewImage)((void)0); |
| 12909 | _visualSearchPreviewImage = image; |
| 12910 | |
| 12911 | ASSERT(!_visualSearchPreviewTitle)((void)0); |
| 12912 | _visualSearchPreviewTitle = title; |
| 12913 | |
| 12914 | ASSERT(!_visualSearchPreviewImageURL)((void)0); |
| 12915 | _visualSearchPreviewImageURL = imageURL; |
| 12916 | |
| 12917 | _visualSearchPreviewImageBounds = imageBounds; |
| 12918 | |
| 12919 | auto currentPresentingViewController = self._wk_viewControllerForFullScreenPresentation; |
| 12920 | [currentPresentingViewController presentViewController:_visualSearchPreviewController.get() animated:YES__objc_yes completion:nilnullptr]; |
| 12921 | } |
| 12922 | |
| 12923 | #pragma mark - QLPreviewControllerDelegate |
| 12924 | |
| 12925 | - (CGRect)previewController:(QLPreviewController *)controller frameForPreviewItem:(id <QLPreviewItem>)item inSourceView:(UIView **)outView |
| 12926 | { |
| 12927 | *outView = self; |
| 12928 | return _visualSearchPreviewImageBounds; |
| 12929 | } |
| 12930 | |
| 12931 | - (UIImage *)previewController:(QLPreviewController *)controller transitionImageForPreviewItem:(id <QLPreviewItem>)item contentRect:(CGRect *)outContentRect |
| 12932 | { |
| 12933 | // FIXME: We should remove this caching when UIKit doesn't call this delegate method twice, with |
| 12934 | // the second call being in the midst of an ongoing animation. See <rdar://problem/131368437>. |
| 12935 | if (!_cachedVisualSearchPreviewImageBoundsInWindowCoordinates) |
| 12936 | _cachedVisualSearchPreviewImageBoundsInWindowCoordinates = { CGPointZero, [self convertRect:_visualSearchPreviewImageBounds toView:nilnullptr].size }; |
| 12937 | *outContentRect = *_cachedVisualSearchPreviewImageBoundsInWindowCoordinates; |
| 12938 | return _visualSearchPreviewImage.get(); |
| 12939 | } |
| 12940 | |
| 12941 | - (void)previewControllerDidDismiss:(QLPreviewController *)controller |
| 12942 | { |
| 12943 | ASSERT(controller == _visualSearchPreviewController)((void)0); |
| 12944 | _visualSearchPreviewController.clear(); |
| 12945 | _visualSearchPreviewImage.clear(); |
| 12946 | _visualSearchPreviewTitle.clear(); |
| 12947 | _visualSearchPreviewImageURL.clear(); |
| 12948 | _cachedVisualSearchPreviewImageBoundsInWindowCoordinates.reset(); |
| 12949 | } |
| 12950 | |
| 12951 | #pragma mark - QLPreviewControllerDataSource |
| 12952 | |
| 12953 | - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller |
| 12954 | { |
| 12955 | ASSERT(controller == _visualSearchPreviewController)((void)0); |
| 12956 | return 1; |
| 12957 | } |
| 12958 | |
| 12959 | - (id <QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index |
| 12960 | { |
| 12961 | ASSERT(controller == _visualSearchPreviewController)((void)0); |
| 12962 | ASSERT(!index)((void)0); |
| 12963 | auto item = adoptNS([PAL::allocQLItemInstance() initWithDataProvider:self contentType:UTTypeTIFF.identifier previewTitle:_visualSearchPreviewTitle.get()]); |
| 12964 | if ([item respondsToSelector:@selector(setPreviewOptions:)]) { |
| 12965 | auto previewOptions = adoptNS([[NSMutableDictionary alloc] initWithCapacity:2]); |
| 12966 | if (_visualSearchPreviewImageURL) |
| 12967 | [previewOptions setObject:_visualSearchPreviewImageURL.get() forKey:@"imageURL"]; |
| 12968 | if (auto pageURL = URL { protect(_page)->currentURL() }; !pageURL.isEmpty()) |
| 12969 | [previewOptions setObject:pageURL.createNSURL().get() forKey:@"pageURL"]; |
| 12970 | if ([previewOptions count]) |
| 12971 | [item setPreviewOptions:previewOptions.get()]; |
| 12972 | } |
| 12973 | return item.autorelease(); |
| 12974 | } |
| 12975 | |
| 12976 | #pragma mark - QLPreviewItemDataProvider |
| 12977 | |
| 12978 | - (NSData *)provideDataForItem:(QLItem *)item |
| 12979 | { |
| 12980 | ASSERT(_visualSearchPreviewImage)((void)0); |
| 12981 | return WebKit::transcode(protect([_visualSearchPreviewImage CGImage]), protect((__bridge CFStringRef)UTTypeTIFF.identifier).get()).autorelease(); |
| 12982 | } |
| 12983 | |
| 12984 | #pragma mark - WKActionSheetAssistantDelegate |
| 12985 | |
| 12986 | - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant shouldIncludeShowTextActionForElement:(_WKActivatedElementInfo *)element |
| 12987 | { |
| 12988 | return WebKit::isLiveTextAvailableAndEnabled() && self.hasSelectableTextForImageContextMenu; |
| 12989 | } |
| 12990 | |
| 12991 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant showTextForImage:(UIImage *)image imageURL:(NSURL *)imageURL title:(NSString *)title imageBounds:(CGRect)imageBounds |
| 12992 | { |
| 12993 | [self presentVisualSearchPreviewControllerForImage:image imageURL:imageURL title:title imageBounds:imageBounds appearanceActions:QLPreviewControllerFirstTimeAppearanceActionEnableVisualSearchDataDetection]; |
| 12994 | } |
| 12995 | |
| 12996 | - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant shouldIncludeLookUpImageActionForElement:(_WKActivatedElementInfo *)element |
| 12997 | { |
| 12998 | return WebKit::isLiveTextAvailableAndEnabled() && self.hasVisualSearchResultsForImageContextMenu; |
| 12999 | } |
| 13000 | |
| 13001 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant lookUpImage:(UIImage *)image imageURL:(NSURL *)imageURL title:(NSString *)title imageBounds:(CGRect)imageBounds |
| 13002 | { |
| 13003 | [self presentVisualSearchPreviewControllerForImage:image imageURL:imageURL title:title imageBounds:imageBounds appearanceActions:QLPreviewControllerFirstTimeAppearanceActionEnableVisualSearchMode]; |
| 13004 | } |
| 13005 | |
| 13006 | #endif // USE(QUICK_LOOK) |
| 13007 | |
| 13008 | #pragma mark - Image Extraction |
| 13009 | |
| 13010 | - (CocoaImageAnalyzer *)imageAnalyzer |
| 13011 | { |
| 13012 | if (!_imageAnalyzer) |
| 13013 | _imageAnalyzer = WebKit::createImageAnalyzer(); |
| 13014 | return _imageAnalyzer.get(); |
| 13015 | } |
| 13016 | |
| 13017 | - (BOOL)hasPendingImageAnalysisRequest |
| 13018 | { |
| 13019 | return !!_pendingImageAnalysisRequestIdentifier; |
| 13020 | } |
| 13021 | |
| 13022 | - (void)_setUpImageAnalysis |
| 13023 | { |
| 13024 | if (!WebKit::isLiveTextAvailableAndEnabled()) |
| 13025 | return; |
| 13026 | |
| 13027 | _pendingImageAnalysisRequestIdentifier = std::nullopt; |
| 13028 | _isProceedingWithTextSelectionInImage = NO__objc_no; |
| 13029 | _elementPendingImageAnalysis = std::nullopt; |
| 13030 | _imageAnalysisGestureRecognizer = adoptNS([[WKImageAnalysisGestureRecognizer alloc] initWithImageAnalysisGestureDelegate:self]); |
| 13031 | [self addGestureRecognizer:_imageAnalysisGestureRecognizer.get()]; |
| 13032 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13033 | _removeBackgroundData = std::nullopt; |
| 13034 | #endif |
| 13035 | _dynamicImageAnalysisContextMenuState = WebKit::DynamicImageAnalysisContextMenuState::NotWaiting; |
| 13036 | _imageAnalysisContextMenuActionData = std::nullopt; |
| 13037 | } |
| 13038 | |
| 13039 | - (void)_tearDownImageAnalysis |
| 13040 | { |
| 13041 | if (!WebKit::isLiveTextAvailableAndEnabled()) |
| 13042 | return; |
| 13043 | |
| 13044 | [_imageAnalysisGestureRecognizer setDelegate:nilnullptr]; |
| 13045 | [self removeGestureRecognizer:_imageAnalysisGestureRecognizer.get()]; |
| 13046 | _imageAnalysisGestureRecognizer = nilnullptr; |
| 13047 | _pendingImageAnalysisRequestIdentifier = std::nullopt; |
| 13048 | _isProceedingWithTextSelectionInImage = NO__objc_no; |
| 13049 | _elementPendingImageAnalysis = std::nullopt; |
| 13050 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 13051 | [std::exchange(_imageAnalyzer, nilnullptr) cancelAllRequests]; |
| 13052 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 13053 | [self _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No]; |
| 13054 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13055 | [self uninstallImageAnalysisInteraction]; |
| 13056 | _removeBackgroundData = std::nullopt; |
| 13057 | #endif |
| 13058 | _dynamicImageAnalysisContextMenuState = WebKit::DynamicImageAnalysisContextMenuState::NotWaiting; |
| 13059 | _imageAnalysisContextMenuActionData = std::nullopt; |
| 13060 | } |
| 13061 | |
| 13062 | - (void)_cancelImageAnalysis |
| 13063 | { |
| 13064 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 13065 | [_imageAnalyzer cancelAllRequests]; |
| 13066 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 13067 | RELEASE_LOG_IF(self.hasPendingImageAnalysisRequest, ImageAnalysis, "Image analysis request %" PRIu64 " cancelled.", _pendingImageAnalysisRequestIdentifier->toUInt64())do { if (self.hasPendingImageAnalysisRequest) clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Image analysis request %" "ll" "u" " cancelled."), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG263" ) = "Image analysis request %" "ll" "u" " cancelled."; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Image analysis request %" "ll" "u" " cancelled.", _pendingImageAnalysisRequestIdentifier ->toUInt64())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Image analysis request %" "ll" "u" " cancelled.", _pendingImageAnalysisRequestIdentifier ->toUInt64()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; } while (0); |
| 13068 | _pendingImageAnalysisRequestIdentifier = std::nullopt; |
| 13069 | _isProceedingWithTextSelectionInImage = NO__objc_no; |
| 13070 | _elementPendingImageAnalysis = std::nullopt; |
| 13071 | } |
| 13072 | |
| 13073 | - (RetainPtr<CocoaImageAnalyzerRequest>)createImageAnalyzerRequest:(VKAnalysisTypes)analysisTypes image:(CGImageRef)image imageURL:(NSURL *)imageURL |
| 13074 | { |
| 13075 | auto request = WebKit::createImageAnalyzerRequest(image, analysisTypes); |
| 13076 | [request setImageURL:imageURL]; |
| 13077 | [request setPageURL:[NSURL _web_URLWithWTFString:protect(_page)->currentURL()]]; |
| 13078 | return request; |
| 13079 | } |
| 13080 | |
| 13081 | - (RetainPtr<CocoaImageAnalyzerRequest>)createImageAnalyzerRequest:(VKAnalysisTypes)analysisTypes image:(CGImageRef)image |
| 13082 | { |
| 13083 | return [self createImageAnalyzerRequest:analysisTypes image:image imageURL:_positionInformation.imageURL.createNSURL().get()]; |
| 13084 | } |
| 13085 | |
| 13086 | |
| 13087 | - (void)updateImageAnalysisForContextMenuPresentation:(CocoaImageAnalysis *)analysis elementBounds:(CGRect)elementBounds |
| 13088 | { |
| 13089 | #if USE(UICONTEXTMENU)(defined 1 && 1) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)(defined 1 && 1) |
| 13090 | analysis.presentingViewControllerForMrcAction = self._wk_viewControllerForFullScreenPresentation; |
| 13091 | if ([analysis respondsToSelector:@selector(setRectForMrcActionInPresentingViewController:)]) { |
| 13092 | CGRect boundsInPresentingViewController = [self convertRect:elementBounds toView:analysis.presentingViewControllerForMrcAction.viewIfLoaded]; |
| 13093 | analysis.rectForMrcActionInPresentingViewController = boundsInPresentingViewController; |
| 13094 | } |
| 13095 | #endif |
| 13096 | } |
| 13097 | |
| 13098 | |
| 13099 | - (BOOL)validateImageAnalysisRequestIdentifier:(WebKit::ImageAnalysisRequestIdentifier)identifier |
| 13100 | { |
| 13101 | if (_pendingImageAnalysisRequestIdentifier == identifier) |
| 13102 | return YES__objc_yes; |
| 13103 | |
| 13104 | if (!self.hasPendingImageAnalysisRequest) { |
| 13105 | // Only invoke deferred image analysis blocks if there is no ongoing request; otherwise, this would |
| 13106 | // cause these blocks to be invoked too early (i.e. in the middle of the new image analysis request). |
| 13107 | [self _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No]; |
| 13108 | } |
| 13109 | |
| 13110 | RELEASE_LOG(ImageAnalysis, "Image analysis request %" PRIu64 " invalidated.", identifier.toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Image analysis request %" "ll" "u" " invalidated."), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG264" ) = "Image analysis request %" "ll" "u" " invalidated."; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Image analysis request %" "ll" "u" " invalidated.", identifier .toUInt64())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Image analysis request %" "ll" "u" " invalidated.", identifier .toUInt64()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13111 | return NO__objc_no; |
| 13112 | } |
| 13113 | |
| 13114 | - (void)requestTextRecognition:(NSURL *)imageURL imageData:(WebCore::ShareableBitmap::Handle&&)imageData sourceLanguageIdentifier:(NSString *)sourceLanguageIdentifier targetLanguageIdentifier:(NSString *)targetLanguageIdentifier completionHandler:(CompletionHandler<void(WebCore::TextRecognitionResult&&)>&&)completion |
| 13115 | { |
| 13116 | auto imageBitmap = WebCore::ShareableBitmap::create(WTF::move(imageData)); |
| 13117 | if (!imageBitmap) { |
| 13118 | completion({ }); |
| 13119 | return; |
| 13120 | } |
| 13121 | |
| 13122 | RetainPtr cgImage = imageBitmap->createPlatformImage(WebCore::DontCopyBackingStore); |
| 13123 | if (!cgImage) { |
| 13124 | completion({ }); |
| 13125 | return; |
| 13126 | } |
| 13127 | |
| 13128 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13129 | if (targetLanguageIdentifier.length) |
| 13130 | return WebKit::requestVisualTranslation(self.imageAnalyzer, imageURL, sourceLanguageIdentifier, targetLanguageIdentifier, cgImage.get(), WTF::move(completion)); |
| 13131 | #else |
| 13132 | UNUSED_PARAM(sourceLanguageIdentifier)(void)sourceLanguageIdentifier; |
| 13133 | UNUSED_PARAM(targetLanguageIdentifier)(void)targetLanguageIdentifier; |
| 13134 | #endif |
| 13135 | |
| 13136 | auto request = [self createImageAnalyzerRequest:VKAnalysisTypeText image:cgImage.get()]; |
| 13137 | [self.imageAnalyzer processRequest:request.get() progressHandler:nilnullptr completionHandler:makeBlockPtr([completion = WTF::move(completion)] (CocoaImageAnalysis *result, NSError *) mutable { |
| 13138 | completion(WebKit::makeTextRecognitionResult(result)); |
| 13139 | }).get()]; |
| 13140 | } |
| 13141 | |
| 13142 | #pragma mark - WKImageAnalysisGestureRecognizerDelegate |
| 13143 | |
| 13144 | - (void)imageAnalysisGestureDidBegin:(WKImageAnalysisGestureRecognizer *)gestureRecognizer |
| 13145 | { |
| 13146 | ASSERT(WebKit::isLiveTextAvailableAndEnabled())((void)0); |
| 13147 | |
| 13148 | if ([self _isPanningScrollViewOrAncestor:gestureRecognizer.lastTouchedScrollView]) |
| 13149 | return; |
| 13150 | |
| 13151 | auto requestIdentifier = WebKit::ImageAnalysisRequestIdentifier::generate(); |
| 13152 | |
| 13153 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 13154 | [_imageAnalyzer cancelAllRequests]; |
| 13155 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 13156 | _pendingImageAnalysisRequestIdentifier = requestIdentifier; |
| 13157 | _isProceedingWithTextSelectionInImage = NO__objc_no; |
| 13158 | _elementPendingImageAnalysis = std::nullopt; |
| 13159 | _dynamicImageAnalysisContextMenuState = WebKit::DynamicImageAnalysisContextMenuState::NotWaiting; |
| 13160 | _imageAnalysisContextMenuActionData = std::nullopt; |
| 13161 | |
| 13162 | WebKit::InteractionInformationRequest request { WebCore::roundedIntPoint([gestureRecognizer locationInView:self]) }; |
| 13163 | request.includeImageData = true; |
| 13164 | [self doAfterPositionInformationUpdate:[requestIdentifier = WTF::move(requestIdentifier), weakSelf = WeakObjCPtr<WKContentView>(self), gestureDeferralToken = WebKit::ImageAnalysisGestureDeferralToken::create(self)] (WebKit::InteractionInformationAtPosition information) mutable { |
| 13165 | auto strongSelf = weakSelf.get(); |
| 13166 | if (![strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier]) |
| 13167 | return; |
| 13168 | |
| 13169 | bool shouldAnalyzeImageAtLocation = ([&] { |
| 13170 | if (!information.isImage && !canAttemptTextRecognitionForNonImageElements(information, protect(strongSelf->_page->preferences()))) |
| 13171 | return false; |
| 13172 | |
| 13173 | if (!information.image) |
| 13174 | return false; |
| 13175 | |
| 13176 | if (!information.hostImageOrVideoElementContext) |
| 13177 | return false; |
| 13178 | |
| 13179 | if (information.isAnimatedImage) |
| 13180 | return false; |
| 13181 | |
| 13182 | if (information.isContentEditable) |
| 13183 | return false; |
| 13184 | |
| 13185 | return true; |
| 13186 | })(); |
| 13187 | |
| 13188 | if (!strongSelf->_pendingImageAnalysisRequestIdentifier || !shouldAnalyzeImageAtLocation) { |
| 13189 | [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No]; |
| 13190 | return; |
| 13191 | } |
| 13192 | |
| 13193 | RetainPtr cgImage = protect(information.image)->createPlatformImage(); |
| 13194 | if (!cgImage) { |
| 13195 | [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No]; |
| 13196 | return; |
| 13197 | } |
| 13198 | |
| 13199 | RELEASE_LOG(ImageAnalysis, "Image analysis preflight gesture initiated (request %" PRIu64 ").", requestIdentifier.toUInt64())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Image analysis preflight gesture initiated (request %" "ll" "u" ")."), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG265" ) = "Image analysis preflight gesture initiated (request %" "ll" "u" ")."; uint8_t _Alignas(16) __attribute__((uninitialized) ) _os_fmt_buf[__builtin_os_log_format_buffer_size("Image analysis preflight gesture initiated (request %" "ll" "u" ").", requestIdentifier.toUInt64())]; _os_log_impl( &__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Image analysis preflight gesture initiated (request %" "ll" "u" ").", requestIdentifier.toUInt64()), (uint32_t)sizeof (_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13200 | |
| 13201 | strongSelf->_elementPendingImageAnalysis = information.hostImageOrVideoElementContext; |
| 13202 | |
| 13203 | auto requestLocation = information.request.point; |
| 13204 | WebCore::ElementContext elementContext = *information.hostImageOrVideoElementContext; |
| 13205 | |
| 13206 | auto requestForTextSelection = [strongSelf createImageAnalyzerRequest:VKAnalysisTypeText image:cgImage.get()]; |
| 13207 | if (information.isPausedVideo) |
| 13208 | [requestForTextSelection setImageSource:VKImageAnalyzerRequestImageSourceVideoFrame]; |
| 13209 | |
| 13210 | if (information.elementContainsImageOverlay) { |
| 13211 | [strongSelf _completeImageAnalysisRequestForContextMenu:cgImage.get() requestIdentifier:requestIdentifier hasTextResults:YES__objc_yes]; |
| 13212 | return; |
| 13213 | } |
| 13214 | |
| 13215 | auto textAnalysisStartTime = MonotonicTime::now(); |
| 13216 | [[strongSelf imageAnalyzer] processRequest:requestForTextSelection.get() progressHandler:nilnullptr completionHandler:[requestIdentifier = WTF::move(requestIdentifier), weakSelf, elementContext, requestLocation, cgImage, gestureDeferralToken, textAnalysisStartTime] (CocoaImageAnalysis *result, NSError *error) mutable { |
| 13217 | auto strongSelf = weakSelf.get(); |
| 13218 | if (![strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier]) |
| 13219 | return; |
| 13220 | |
| 13221 | BOOL hasTextResults = [result hasResultsForAnalysisTypes:VKAnalysisTypeText]; |
| 13222 | RELEASE_LOG(ImageAnalysis, "Image analysis completed in %.0f ms (request %" PRIu64 "; found text? %d)", (MonotonicTime::now() - textAnalysisStartTime).milliseconds(), requestIdentifier.toUInt64(), hasTextResults)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Image analysis completed in %.0f ms (request %" "ll" "u" "; found text? %d)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG266" ) = "Image analysis completed in %.0f ms (request %" "ll" "u" "; found text? %d)"; uint8_t _Alignas(16) __attribute__((uninitialized )) _os_fmt_buf[__builtin_os_log_format_buffer_size("Image analysis completed in %.0f ms (request %" "ll" "u" "; found text? %d)", (MonotonicTime::now() - textAnalysisStartTime ).milliseconds(), requestIdentifier.toUInt64(), hasTextResults )]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str , (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Image analysis completed in %.0f ms (request %" "ll" "u" "; found text? %d)", (MonotonicTime::now() - textAnalysisStartTime ).milliseconds(), requestIdentifier.toUInt64(), hasTextResults ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13223 | |
| 13224 | protect(strongSelf->_page)->updateWithTextRecognitionResult(WebKit::makeTextRecognitionResult(result), elementContext, requestLocation, [requestIdentifier = WTF::move(requestIdentifier), weakSelf, hasTextResults, cgImage, gestureDeferralToken] (WebKit::TextRecognitionUpdateResult updateResult) mutable { |
| 13225 | auto strongSelf = weakSelf.get(); |
| 13226 | if (![strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier]) |
| 13227 | return; |
| 13228 | |
| 13229 | if (updateResult == WebKit::TextRecognitionUpdateResult::Text) { |
| 13230 | strongSelf->_isProceedingWithTextSelectionInImage = YES__objc_yes; |
| 13231 | [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::Yes]; |
| 13232 | return; |
| 13233 | } |
| 13234 | |
| 13235 | gestureDeferralToken->setShouldPreventTextSelection(); |
| 13236 | |
| 13237 | if (updateResult == WebKit::TextRecognitionUpdateResult::DataDetector) { |
| 13238 | [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No]; |
| 13239 | return; |
| 13240 | } |
| 13241 | |
| 13242 | [strongSelf _completeImageAnalysisRequestForContextMenu:cgImage.get() requestIdentifier:requestIdentifier hasTextResults:hasTextResults]; |
| 13243 | }); |
| 13244 | }]; |
| 13245 | } forRequest:request]; |
| 13246 | } |
| 13247 | |
| 13248 | #if ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)(defined 1 && 1) |
| 13249 | static BOOL shouldUseMachineReadableCodeMenuFromImageAnalysisResult(CocoaImageAnalysis *result) |
| 13250 | { |
| 13251 | #if HAVE(BCS_LIVE_CAMERA_ONLY_ACTION_SPI)(defined HAVE_BCS_LIVE_CAMERA_ONLY_ACTION_SPI && HAVE_BCS_LIVE_CAMERA_ONLY_ACTION_SPI ) |
| 13252 | return [result.barcodeActions indexOfObjectPassingTest:^BOOL(BCSAction *action, NSUInteger index, BOOL *stop) { |
| 13253 | return [action respondsToSelector:@selector(isLiveCameraOnlyAction)] && [(id)action isLiveCameraOnlyAction]; |
| 13254 | }] == NSNotFound; |
| 13255 | #else // not HAVE(BCS_LIVE_CAMERA_ONLY_ACTION_SPI) |
| 13256 | return YES__objc_yes; |
| 13257 | #endif // HAVE(BCS_LIVE_CAMERA_ONLY_ACTION_SPI) |
| 13258 | } |
| 13259 | #endif // ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES) |
| 13260 | |
| 13261 | - (void)_completeImageAnalysisRequestForContextMenu:(CGImageRef)image requestIdentifier:(WebKit::ImageAnalysisRequestIdentifier)requestIdentifier hasTextResults:(BOOL)hasTextResults |
| 13262 | { |
| 13263 | _dynamicImageAnalysisContextMenuState = WebKit::DynamicImageAnalysisContextMenuState::WaitingForImageAnalysis; |
| 13264 | _imageAnalysisContextMenuActionData = std::nullopt; |
| 13265 | [self _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No]; |
| 13266 | |
| 13267 | auto data = Box<WebKit::ImageAnalysisContextMenuActionData>::create(); |
| 13268 | data->hasSelectableText = hasTextResults; |
| 13269 | |
| 13270 | auto weakSelf = WeakObjCPtr<WKContentView>(self); |
| 13271 | auto aggregator = MainRunLoopCallbackAggregator::create([weakSelf, data]() mutable { |
| 13272 | auto strongSelf = weakSelf.get(); |
| 13273 | if (!strongSelf || strongSelf->_dynamicImageAnalysisContextMenuState == WebKit::DynamicImageAnalysisContextMenuState::NotWaiting) |
| 13274 | return; |
| 13275 | |
| 13276 | strongSelf->_imageAnalysisContextMenuActionData = WTF::move(*data); |
| 13277 | [strongSelf _insertDynamicImageAnalysisContextMenuItemsIfPossible]; |
| 13278 | }); |
| 13279 | |
| 13280 | auto request = [self createImageAnalyzerRequest:VKAnalysisTypeVisualSearch | VKAnalysisTypeMachineReadableCode | VKAnalysisTypeAppClip image:image]; |
| 13281 | if (_positionInformation.isPausedVideo) |
| 13282 | [request setImageSource:VKImageAnalyzerRequestImageSourceVideoFrame]; |
| 13283 | |
| 13284 | auto elementBounds = _positionInformation.bounds; |
| 13285 | auto visualSearchAnalysisStartTime = MonotonicTime::now(); |
| 13286 | [self.imageAnalyzer processRequest:request.get() progressHandler:nilnullptr completionHandler:[requestIdentifier = WTF::move(requestIdentifier), weakSelf, visualSearchAnalysisStartTime, aggregator = aggregator.copyRef(), data, elementBounds] (CocoaImageAnalysis *result, NSError *error) mutable { |
| 13287 | auto strongSelf = weakSelf.get(); |
| 13288 | if (!strongSelf) |
| 13289 | return; |
| 13290 | |
| 13291 | #if USE(QUICK_LOOK)(defined 1 && 1) |
| 13292 | BOOL hasVisualSearchResults = [result hasResultsForAnalysisTypes:VKAnalysisTypeVisualSearch]; |
| 13293 | RELEASE_LOG(ImageAnalysis, "Image analysis completed in %.0f ms (request %" PRIu64 "; found visual search results? %d)", (MonotonicTime::now() - visualSearchAnalysisStartTime).milliseconds(), requestIdentifier.toUInt64(), hasVisualSearchResults)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Image analysis completed in %.0f ms (request %" "ll" "u" "; found visual search results? %d)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG267" ) = "Image analysis completed in %.0f ms (request %" "ll" "u" "; found visual search results? %d)"; uint8_t _Alignas(16) __attribute__ ((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Image analysis completed in %.0f ms (request %" "ll" "u" "; found visual search results? %d)" , (MonotonicTime::now() - visualSearchAnalysisStartTime).milliseconds (), requestIdentifier.toUInt64(), hasVisualSearchResults)]; _os_log_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "Image analysis completed in %.0f ms (request %" "ll" "u" "; found visual search results? %d)", (MonotonicTime ::now() - visualSearchAnalysisStartTime).milliseconds(), requestIdentifier .toUInt64(), hasVisualSearchResults), (uint32_t)sizeof(_os_fmt_buf )) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13294 | #else |
| 13295 | UNUSED_PARAM(requestIdentifier)(void)requestIdentifier; |
| 13296 | UNUSED_PARAM(visualSearchAnalysisStartTime)(void)visualSearchAnalysisStartTime; |
| 13297 | #endif |
| 13298 | if (!result || error) |
| 13299 | return; |
| 13300 | |
| 13301 | [strongSelf updateImageAnalysisForContextMenuPresentation:result elementBounds:elementBounds]; |
| 13302 | |
| 13303 | #if ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)(defined 1 && 1) |
| 13304 | if (shouldUseMachineReadableCodeMenuFromImageAnalysisResult(result)) |
| 13305 | data->machineReadableCodeMenu = result.mrcMenu; |
| 13306 | #endif |
| 13307 | #if USE(QUICK_LOOK)(defined 1 && 1) |
| 13308 | data->hasVisualSearchResults = hasVisualSearchResults; |
| 13309 | #endif |
| 13310 | }]; |
| 13311 | |
| 13312 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13313 | if (protect(_page->preferences())->removeBackgroundEnabled()) { |
| 13314 | WebKit::requestBackgroundRemoval(image, [weakSelf = WeakObjCPtr<WKContentView>(self), aggregator = aggregator.copyRef(), data](CGImageRef result) mutable { |
| 13315 | if (auto strongSelf = weakSelf.get()) |
| 13316 | data->copySubjectResult = result; |
| 13317 | }); |
| 13318 | } |
| 13319 | #endif |
| 13320 | } |
| 13321 | |
| 13322 | - (void)_insertDynamicImageAnalysisContextMenuItemsIfPossible |
| 13323 | { |
| 13324 | bool updated = false; |
| 13325 | [self.contextMenuInteraction updateVisibleMenuWithBlock:makeBlockPtr([&, strongSelf = RetainPtr { self }](UIMenu *menu) -> UIMenu * { |
| 13326 | updated = true; |
| 13327 | __block__attribute__((__blocks__(byref))) BOOL foundRevealImageItem = NO__objc_no; |
| 13328 | __block__attribute__((__blocks__(byref))) BOOL foundShowTextItem = NO__objc_no; |
| 13329 | RetainPtr revealImageIdentifier = elementActionTypeToUIActionIdentifier(_WKElementActionTypeRevealImage); |
| 13330 | RetainPtr showTextIdentifier = elementActionTypeToUIActionIdentifier(_WKElementActionTypeImageExtraction); |
| 13331 | [menu.children enumerateObjectsUsingBlock:^(UIMenuElement *child, NSUInteger index, BOOL* stop) { |
| 13332 | auto *action = dynamic_objc_cast<UIAction>(child); |
| 13333 | if ([action.identifier isEqualToString:revealImageIdentifier.get()]) |
| 13334 | foundRevealImageItem = YES__objc_yes; |
| 13335 | else if ([action.identifier isEqualToString:showTextIdentifier.get()]) |
| 13336 | foundShowTextItem = YES__objc_yes; |
| 13337 | }]; |
| 13338 | |
| 13339 | auto adjustedChildren = adoptNS(menu.children.mutableCopy); |
| 13340 | auto newItems = adoptNS([NSMutableArray<UIMenuElement *> new]); |
| 13341 | |
| 13342 | for (UIMenuElement *child in adjustedChildren.get()) { |
| 13343 | RetainPtr action = dynamic_objc_cast<UIAction>(child); |
| 13344 | if (!action) |
| 13345 | continue; |
| 13346 | |
| 13347 | if ([action.get().identifier isEqual:protect(elementActionTypeToUIActionIdentifier(_WKElementActionTypeCopyCroppedImage)).get()]) { |
| 13348 | if (self.subjectResultForImageContextMenu) |
| 13349 | action.get().attributes &= ~UIMenuElementAttributesDisabled; |
| 13350 | |
| 13351 | continue; |
| 13352 | } |
| 13353 | |
| 13354 | if ([action.get().identifier isEqual:revealImageIdentifier.get()]) { |
| 13355 | if (self.hasVisualSearchResultsForImageContextMenu) |
| 13356 | action.get().attributes &= ~UIMenuElementAttributesDisabled; |
| 13357 | |
| 13358 | continue; |
| 13359 | } |
| 13360 | } |
| 13361 | |
| 13362 | if (!foundShowTextItem && self.hasSelectableTextForImageContextMenu) { |
| 13363 | // Dynamically insert the "Show Text" menu item, if it wasn't already inserted. |
| 13364 | // FIXME: This should probably be inserted unconditionally, and enabled if needed |
| 13365 | // like Look Up or Copy Subject. |
| 13366 | auto *elementInfo = [_WKActivatedElementInfo activatedElementInfoWithInteractionInformationAtPosition:_positionInformation userInfo:nilnullptr]; |
| 13367 | auto *elementAction = [_WKElementAction _elementActionWithType:_WKElementActionTypeImageExtraction info:elementInfo assistant:_actionSheetAssistant.get()]; |
| 13368 | [newItems addObject:[elementAction uiActionForElementInfo:elementInfo]]; |
| 13369 | } |
| 13370 | |
| 13371 | if (foundRevealImageItem) { |
| 13372 | // Only dynamically insert machine-readable code items if the client didn't explicitly |
| 13373 | // remove the Look Up ("reveal image") item. |
| 13374 | if (UIMenu *subMenu = self.machineReadableCodeSubMenuForImageContextMenu) |
| 13375 | [newItems addObject:subMenu]; |
| 13376 | } |
| 13377 | |
| 13378 | RELEASE_LOG(ImageAnalysis, "Dynamically inserting %zu context menu action(s)", [newItems count])clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Dynamically inserting %zu context menu action(s)"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG268" ) = "Dynamically inserting %zu context menu action(s)"; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Dynamically inserting %zu context menu action(s)", [newItems count])]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Dynamically inserting %zu context menu action(s)", [newItems count]), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13379 | [adjustedChildren addObjectsFromArray:newItems.get()]; |
| 13380 | return [menu menuByReplacingChildren:adjustedChildren.get()]; |
| 13381 | }).get()]; |
| 13382 | |
| 13383 | _dynamicImageAnalysisContextMenuState = updated ? WebKit::DynamicImageAnalysisContextMenuState::NotWaiting : WebKit::DynamicImageAnalysisContextMenuState::WaitingForVisibleMenu; |
| 13384 | } |
| 13385 | |
| 13386 | - (void)imageAnalysisGestureDidFail:(WKImageAnalysisGestureRecognizer *)gestureRecognizer |
| 13387 | { |
| 13388 | [self _endImageAnalysisGestureDeferral:WebKit::ShouldPreventGestures::No]; |
| 13389 | } |
| 13390 | |
| 13391 | - (void)captureTextFromCameraForWebView:(id)sender |
| 13392 | { |
| 13393 | [super captureTextFromCamera:sender]; |
| 13394 | } |
| 13395 | |
| 13396 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 13397 | |
| 13398 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13399 | |
| 13400 | - (BOOL)actionSheetAssistantShouldIncludeCopySubjectAction:(WKActionSheetAssistant *)assistant |
| 13401 | { |
| 13402 | return !!self.subjectResultForImageContextMenu; |
| 13403 | } |
| 13404 | |
| 13405 | - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant copySubject:(UIImage *)image sourceMIMEType:(NSString *)sourceMIMEType |
| 13406 | { |
| 13407 | if (!self.subjectResultForImageContextMenu) |
| 13408 | return; |
| 13409 | |
| 13410 | auto [data, type] = WebKit::imageDataForRemoveBackground(protect(self.subjectResultForImageContextMenu), protect((__bridge CFStringRef)sourceMIMEType).get()); |
| 13411 | if (!data) |
| 13412 | return; |
| 13413 | |
| 13414 | [UIPasteboard _performAsDataOwner:self._dataOwnerForCopy block:[data = WTF::move(data), type = WTF::move(type)] { |
| 13415 | [UIPasteboard.generalPasteboard setData:data.get() forPasteboardType:(__bridge NSString *)type.get()]; |
| 13416 | }]; |
| 13417 | } |
| 13418 | |
| 13419 | #pragma mark - VKCImageAnalysisInteractionDelegate |
| 13420 | |
| 13421 | - (CGRect)contentsRectForImageAnalysisInteraction:(VKCImageAnalysisInteraction *)interaction |
| 13422 | { |
| 13423 | auto unitInteractionRect = _imageAnalysisInteractionBounds; |
| 13424 | WebCore::FloatRect contentViewBounds = self.bounds; |
| 13425 | unitInteractionRect.moveBy(-contentViewBounds.location()); |
| 13426 | unitInteractionRect.scale(1 / contentViewBounds.size()); |
| 13427 | return unitInteractionRect; |
| 13428 | } |
| 13429 | |
| 13430 | - (BOOL)imageAnalysisInteraction:(VKCImageAnalysisInteraction *)interaction shouldBeginAtPoint:(CGPoint)point forAnalysisType:(VKImageAnalysisInteractionTypes)analysisType |
| 13431 | { |
| 13432 | return interaction.hasActiveTextSelection || [interaction interactableItemExistsAtPoint:point]; |
| 13433 | } |
| 13434 | |
| 13435 | #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| 13436 | |
| 13437 | - (void)beginTextRecognitionForFullscreenVideo:(WebCore::ShareableBitmap::Handle&&)imageData playerViewController:(AVPlayerViewController *)controller |
| 13438 | { |
| 13439 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13440 | ASSERT(_page->preferences().textRecognitionInVideosEnabled())((void)0); |
| 13441 | |
| 13442 | if (_fullscreenVideoImageAnalysisRequestIdentifier) |
| 13443 | return; |
| 13444 | |
| 13445 | auto imageBitmap = WebCore::ShareableBitmap::create(WTF::move(imageData)); |
| 13446 | if (!imageBitmap) |
| 13447 | return; |
| 13448 | |
| 13449 | RetainPtr cgImage = imageBitmap->createPlatformImage(WebCore::DontCopyBackingStore); |
| 13450 | if (!cgImage) |
| 13451 | return; |
| 13452 | |
| 13453 | auto request = [self createImageAnalyzerRequest:WebKit::analysisTypesForFullscreenVideo() image:cgImage.get()]; |
| 13454 | [request setImageSource:VKImageAnalyzerRequestImageSourceVideoFrame]; |
| 13455 | _fullscreenVideoImageAnalysisRequestIdentifier = [self.imageAnalyzer processRequest:request.get() progressHandler:nilnullptr completionHandler:makeBlockPtr([weakSelf = WeakObjCPtr<WKContentView>(self), controller = RetainPtr { controller }] (CocoaImageAnalysis *result, NSError *) mutable { |
| 13456 | auto strongSelf = weakSelf.get(); |
| 13457 | if (!strongSelf) |
| 13458 | return; |
| 13459 | |
| 13460 | strongSelf->_fullscreenVideoImageAnalysisRequestIdentifier = 0; |
| 13461 | |
| 13462 | if ([controller respondsToSelector:@selector(setImageAnalysis:)]) |
| 13463 | [controller setImageAnalysis:result]; |
| 13464 | }).get()]; |
| 13465 | #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| 13466 | } |
| 13467 | |
| 13468 | - (void)cancelTextRecognitionForFullscreenVideo:(AVPlayerViewController *)controller |
| 13469 | { |
| 13470 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13471 | if (auto identifier = std::exchange(_fullscreenVideoImageAnalysisRequestIdentifier, 0)) |
| 13472 | [_imageAnalyzer cancelRequestID:identifier]; |
| 13473 | |
| 13474 | if ([controller respondsToSelector:@selector(setImageAnalysis:)]) |
| 13475 | [controller setImageAnalysis:nilnullptr]; |
| 13476 | #endif |
| 13477 | } |
| 13478 | |
| 13479 | - (BOOL)isTextRecognitionInFullscreenVideoEnabled |
| 13480 | { |
| 13481 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13482 | return protect(_page->preferences())->textRecognitionInVideosEnabled(); |
| 13483 | #else |
| 13484 | return NO__objc_no; |
| 13485 | #endif |
| 13486 | } |
| 13487 | |
| 13488 | - (void)beginTextRecognitionForVideoInElementFullscreen:(WebCore::ShareableBitmap::Handle&&)bitmapHandle bounds:(WebCore::FloatRect)bounds |
| 13489 | { |
| 13490 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13491 | auto imageBitmap = WebCore::ShareableBitmap::create(WTF::move(bitmapHandle)); |
| 13492 | if (!imageBitmap) |
| 13493 | return; |
| 13494 | |
| 13495 | RetainPtr image = imageBitmap->createPlatformImage(WebCore::DontCopyBackingStore); |
| 13496 | if (!image) |
| 13497 | return; |
| 13498 | |
| 13499 | auto request = WebKit::createImageAnalyzerRequest(image.get(), WebKit::analysisTypesForElementFullscreenVideo()); |
| 13500 | [request setImageSource:VKImageAnalyzerRequestImageSourceVideoFrame]; |
| 13501 | _fullscreenVideoImageAnalysisRequestIdentifier = [self.imageAnalyzer processRequest:request.get() progressHandler:nilnullptr completionHandler:[weakSelf = WeakObjCPtr<WKContentView>(self), bounds](CocoaImageAnalysis *result, NSError *error) { |
| 13502 | auto strongSelf = weakSelf.get(); |
| 13503 | if (!strongSelf || !strongSelf->_fullscreenVideoImageAnalysisRequestIdentifier) |
| 13504 | return; |
| 13505 | |
| 13506 | strongSelf->_fullscreenVideoImageAnalysisRequestIdentifier = 0; |
| 13507 | if (error || !result) |
| 13508 | return; |
| 13509 | |
| 13510 | strongSelf->_imageAnalysisInteractionBounds = bounds; |
| 13511 | [strongSelf installImageAnalysisInteraction:result]; |
| 13512 | }]; |
| 13513 | #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| 13514 | } |
| 13515 | |
| 13516 | - (void)cancelTextRecognitionForVideoInElementFullscreen |
| 13517 | { |
| 13518 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13519 | [self uninstallImageAnalysisInteraction]; |
| 13520 | |
| 13521 | if (auto previousIdentifier = std::exchange(_fullscreenVideoImageAnalysisRequestIdentifier, 0)) |
| 13522 | [self.imageAnalyzer cancelRequestID:previousIdentifier]; |
| 13523 | #endif |
| 13524 | } |
| 13525 | |
| 13526 | #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13527 | |
| 13528 | - (void)installImageAnalysisInteraction:(VKCImageAnalysis *)analysis |
| 13529 | { |
| 13530 | if (!_imageAnalysisInteraction) { |
| 13531 | _imageAnalysisActionButtons = adoptNS([[NSMutableSet alloc] initWithCapacity:1]); |
| 13532 | _imageAnalysisInteraction = adoptNS([PAL::allocVKCImageAnalysisInteractionInstance() init]); |
| 13533 | [_imageAnalysisInteraction setDelegate:self]; |
| 13534 | [_imageAnalysisInteraction setAnalysisButtonRequiresVisibleContentGating:YES__objc_yes]; |
| 13535 | [_imageAnalysisInteraction setQuickActionConfigurationUpdateHandler:[weakSelf = WeakObjCPtr<WKContentView>(self)] (UIButton *button) { |
| 13536 | if (auto strongSelf = weakSelf.get()) |
| 13537 | [strongSelf->_imageAnalysisActionButtons addObject:button]; |
| 13538 | }]; |
| 13539 | WebKit::prepareImageAnalysisForOverlayView(_imageAnalysisInteraction.get()); |
| 13540 | [self addInteraction:_imageAnalysisInteraction.get()]; |
| 13541 | RELEASE_LOG(ImageAnalysis, "Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}",clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG269" ) = "Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" , _imageAnalysisInteractionBounds.x(), _imageAnalysisInteractionBounds .y(), _imageAnalysisInteractionBounds.width(), _imageAnalysisInteractionBounds .height())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" , _imageAnalysisInteractionBounds.x(), _imageAnalysisInteractionBounds .y(), _imageAnalysisInteractionBounds.width(), _imageAnalysisInteractionBounds .height()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop |
| 13542 | _imageAnalysisInteractionBounds.x(), _imageAnalysisInteractionBounds.y(), _imageAnalysisInteractionBounds.width(), _imageAnalysisInteractionBounds.height())clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG269" ) = "Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" , _imageAnalysisInteractionBounds.x(), _imageAnalysisInteractionBounds .y(), _imageAnalysisInteractionBounds.width(), _imageAnalysisInteractionBounds .height())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Installing image analysis interaction at {{ %.0f, %.0f }, { %.0f, %.0f }}" , _imageAnalysisInteractionBounds.x(), _imageAnalysisInteractionBounds .y(), _imageAnalysisInteractionBounds.width(), _imageAnalysisInteractionBounds .height()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13543 | } |
| 13544 | [_imageAnalysisInteraction setAnalysis:analysis]; |
| 13545 | [_imageAnalysisDeferringGestureRecognizer setEnabled:NO__objc_no]; |
| 13546 | [_imageAnalysisGestureRecognizer setEnabled:NO__objc_no]; |
| 13547 | } |
| 13548 | |
| 13549 | - (void)uninstallImageAnalysisInteraction |
| 13550 | { |
| 13551 | if (!_imageAnalysisInteraction) |
| 13552 | return; |
| 13553 | |
| 13554 | RELEASE_LOG(ImageAnalysis, "Uninstalling image analysis interaction")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogImageAnalysis .osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT ); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__ ({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Uninstalling image analysis interaction"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG270" ) = "Uninstalling image analysis interaction"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("Uninstalling image analysis interaction")]; _os_log_impl(& __dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format (_os_fmt_buf, "Uninstalling image analysis interaction"), (uint32_t )sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13555 | |
| 13556 | [self removeInteraction:_imageAnalysisInteraction.get()]; |
| 13557 | [_imageAnalysisInteraction setDelegate:nilnullptr]; |
| 13558 | [_imageAnalysisInteraction setQuickActionConfigurationUpdateHandler:nilnullptr]; |
| 13559 | _imageAnalysisInteraction = nilnullptr; |
| 13560 | _imageAnalysisActionButtons = nilnullptr; |
| 13561 | _imageAnalysisInteractionBounds = { }; |
| 13562 | [_imageAnalysisDeferringGestureRecognizer setEnabled:WebKit::isLiveTextAvailableAndEnabled()]; |
| 13563 | [_imageAnalysisGestureRecognizer setEnabled:WebKit::isLiveTextAvailableAndEnabled()]; |
| 13564 | } |
| 13565 | |
| 13566 | #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| 13567 | |
| 13568 | - (BOOL)_shouldAvoidSecurityHeuristicScoreUpdates |
| 13569 | { |
| 13570 | // FIXME: The whole security heuristic thing should be a USE/HAVE. |
| 13571 | #if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION) |
| 13572 | return YES__objc_yes; |
| 13573 | #elif ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1) |
| 13574 | return [_imageAnalysisInteraction hasActiveTextSelection]; |
| 13575 | #else |
| 13576 | return NO__objc_no; |
| 13577 | #endif |
| 13578 | } |
| 13579 | |
| 13580 | #pragma mark - _UITextInputTranslationSupport |
| 13581 | |
| 13582 | - (BOOL)isImageBacked |
| 13583 | { |
| 13584 | return _page && protect(_page)->editorState().selectionIsRangeInsideImageOverlay; |
| 13585 | } |
| 13586 | |
| 13587 | #if HAVE(UI_EDIT_MENU_INTERACTION)(defined 1 && 1) |
| 13588 | |
| 13589 | - (void)willPresentEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator |
| 13590 | { |
| 13591 | [animator addCompletion:[weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 13592 | if (auto strongSelf = weakSelf.get()) |
| 13593 | strongSelf->_isPresentingEditMenu = YES__objc_yes; |
| 13594 | }]; |
| 13595 | |
| 13596 | auto delegate = self.webView.UIDelegate; |
| 13597 | if (![delegate respondsToSelector:@selector(webView:willPresentEditMenuWithAnimator:)]) |
| 13598 | return; |
| 13599 | |
| 13600 | [delegate webView:self.webView willPresentEditMenuWithAnimator:animator]; |
| 13601 | } |
| 13602 | |
| 13603 | - (void)willDismissEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator |
| 13604 | { |
| 13605 | [animator addCompletion:[weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 13606 | if (auto strongSelf = weakSelf.get()) |
| 13607 | strongSelf->_isPresentingEditMenu = NO__objc_no; |
| 13608 | }]; |
| 13609 | |
| 13610 | auto delegate = self.webView.UIDelegate; |
| 13611 | if (![delegate respondsToSelector:@selector(webView:willDismissEditMenuWithAnimator:)]) |
| 13612 | return; |
| 13613 | |
| 13614 | [delegate webView:self.webView willDismissEditMenuWithAnimator:animator]; |
| 13615 | } |
| 13616 | |
| 13617 | #endif // HAVE(UI_EDIT_MENU_INTERACTION) |
| 13618 | |
| 13619 | - (BOOL)isPresentingEditMenu |
| 13620 | { |
| 13621 | return _isPresentingEditMenu; |
| 13622 | } |
| 13623 | |
| 13624 | - (CGSize)sizeForLegacyFormControlPickerViews |
| 13625 | { |
| 13626 | auto size = self.window.bounds.size; |
| 13627 | size.height = 0; // Fall back to the default input view height. |
| 13628 | return size; |
| 13629 | } |
| 13630 | |
| 13631 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 13632 | |
| 13633 | #pragma mark - BETextInput (and related) |
| 13634 | |
| 13635 | - (id<BETextInput>)asBETextInput |
| 13636 | { |
| 13637 | ASSERT([self conformsToProtocol:@protocol(BETextInput)])((void)0); |
| 13638 | return static_cast<id<BETextInput>>(self); |
| 13639 | } |
| 13640 | |
| 13641 | - (void)_logMissingSystemInputDelegateIfNeeded:(const char*)methodName |
| 13642 | { |
| 13643 | if (_asyncInputDelegate) |
| 13644 | return; |
| 13645 | |
| 13646 | static constexpr auto delayBetweenLogStatements = 10_s; |
| 13647 | static ApproximateTime lastLoggingTimestamp; |
| 13648 | if (auto timestamp = ApproximateTime::now(); timestamp - lastLoggingTimestamp > delayBetweenLogStatements) { |
| 13649 | RELEASE_LOG_ERROR(TextInput, "%{public}s - system input delegate is nil", methodName)clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInput .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("%{public}s - system input delegate is nil"), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG271" ) = "%{public}s - system input delegate is nil"; uint8_t _Alignas (16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size ("%{public}s - system input delegate is nil", methodName)]; _os_log_error_impl (&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "%{public}s - system input delegate is nil" , methodName), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 13650 | lastLoggingTimestamp = timestamp; |
| 13651 | } |
| 13652 | } |
| 13653 | |
| 13654 | - (void)shiftKeyStateChangedFromState:(BEKeyModifierFlags)oldState toState:(BEKeyModifierFlags)newState |
| 13655 | { |
| 13656 | ASSERT(oldState != newState)((void)0); |
| 13657 | if (_isHandlingActivePressesEvent) { |
| 13658 | // UIKit will call into us to handle the individual events, so there's no need to dispatch |
| 13659 | // these synthetic modifier change key events. |
| 13660 | return; |
| 13661 | } |
| 13662 | |
| 13663 | auto dispatchSyntheticFlagsChangedEvents = [&] (BEKeyModifierFlags state, bool keyDown) { |
| 13664 | RetainPtr<WKSyntheticFlagsChangedWebEvent> syntheticEvent; |
| 13665 | switch (state) { |
| 13666 | case BEKeyModifierFlagNone: |
| 13667 | return; |
| 13668 | case BEKeyModifierFlagShift: |
| 13669 | syntheticEvent = adoptNS([[WKSyntheticFlagsChangedWebEvent alloc] initWithShiftState:keyDown]); |
| 13670 | break; |
| 13671 | case BEKeyModifierFlagCapsLock: |
| 13672 | syntheticEvent = adoptNS([[WKSyntheticFlagsChangedWebEvent alloc] initWithCapsLockState:keyDown]); |
| 13673 | break; |
| 13674 | } |
| 13675 | [self _internalHandleKeyWebEvent:syntheticEvent.get()]; |
| 13676 | }; |
| 13677 | |
| 13678 | dispatchSyntheticFlagsChangedEvents(oldState, false); |
| 13679 | dispatchSyntheticFlagsChangedEvents(newState, true); |
| 13680 | } |
| 13681 | |
| 13682 | - (void)systemWillPresentEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator |
| 13683 | { |
| 13684 | [self willPresentEditMenuWithAnimator:animator]; |
| 13685 | } |
| 13686 | |
| 13687 | - (void)systemWillDismissEditMenuWithAnimator:(id<UIEditMenuInteractionAnimating>)animator |
| 13688 | { |
| 13689 | [self willDismissEditMenuWithAnimator:animator]; |
| 13690 | } |
| 13691 | |
| 13692 | - (void)insertTextAlternatives:(PlatformTextAlternativesBETextAlternatives *)alternatives |
| 13693 | { |
| 13694 | auto text = alternatives.primaryString; |
| 13695 | if (!alternatives.alternativeStrings.count) { |
| 13696 | [self insertText:text]; |
| 13697 | return; |
| 13698 | } |
| 13699 | |
| 13700 | WebCore::TextAlternativeWithRange textAlternativeWithRange { alternatives, NSMakeRange(0, text.length) }; |
| 13701 | |
| 13702 | protect(_page)->insertDictatedTextAsync(text, { }, { textAlternativeWithRange }, WebKit::InsertTextOptions { |
| 13703 | .shouldSimulateKeyboardInput = static_cast<bool>([self _shouldSimulateKeyboardInputOnTextInsertion]) |
| 13704 | }); |
| 13705 | |
| 13706 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 13707 | } |
| 13708 | |
| 13709 | - (void)insertText:(NSString *)text textAlternatives:(PlatformTextAlternativesBETextAlternatives *)alternatives style:(UITextAlternativeStyle)style |
| 13710 | { |
| 13711 | [self insertTextAlternatives:alternatives]; |
| 13712 | } |
| 13713 | |
| 13714 | - (BOOL)isPointNearMarkedText:(CGPoint)point |
| 13715 | { |
| 13716 | return [self pointIsNearMarkedText:point]; |
| 13717 | } |
| 13718 | |
| 13719 | - (void)replaceSelectedText:(NSString *)text withText:(NSString *)replacementText |
| 13720 | { |
| 13721 | [self replaceText:text withText:replacementText]; |
| 13722 | } |
| 13723 | |
| 13724 | - (void)updateCurrentSelectionTo:(CGPoint)point fromGesture:(WKBEGestureTypeBEGestureType)gestureType inState:(UIGestureRecognizerState)state |
| 13725 | { |
| 13726 | [self changeSelectionWithGestureAt:point withGesture:gestureType withState:state]; |
| 13727 | } |
| 13728 | |
| 13729 | - (void)setSelectionFromPoint:(CGPoint)from toPoint:(CGPoint)to gesture:(WKBEGestureTypeBEGestureType)gestureType state:(UIGestureRecognizerState)gestureState |
| 13730 | { |
| 13731 | [self changeSelectionWithTouchesFrom:from to:to withGesture:gestureType withState:gestureState]; |
| 13732 | } |
| 13733 | |
| 13734 | - (void)adjustSelectionBoundaryToPoint:(CGPoint)point touchPhase:(WKBESelectionTouchPhaseBESelectionTouchPhase)touch baseIsStart:(BOOL)boundaryIsStart flags:(WKBESelectionFlagsBESelectionFlags)flags |
| 13735 | { |
| 13736 | [self changeSelectionWithTouchAt:point withSelectionTouch:touch baseIsStart:boundaryIsStart withFlags:flags]; |
| 13737 | } |
| 13738 | |
| 13739 | - (void)updateSelectionWithExtentPoint:(CGPoint)point boundary:(UITextGranularity)granularity completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler |
| 13740 | { |
| 13741 | [self updateSelectionWithExtentPoint:point withBoundary:granularity completionHandler:completionHandler]; |
| 13742 | } |
| 13743 | |
| 13744 | - (void)selectTextInGranularity:(UITextGranularity)granularity atPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler |
| 13745 | { |
| 13746 | [self selectTextWithGranularity:granularity atPoint:point completionHandler:completionHandler]; |
| 13747 | } |
| 13748 | |
| 13749 | - (void)moveSelectionAtBoundary:(UITextGranularity)granularity inStorageDirection:(UITextStorageDirection)direction completionHandler:(void (^)(void))completionHandler |
| 13750 | { |
| 13751 | [self moveSelectionAtBoundary:granularity inDirection:direction completionHandler:completionHandler]; |
| 13752 | } |
| 13753 | |
| 13754 | - (id<BEExtendedTextInputTraits>)extendedTextInputTraits |
| 13755 | { |
| 13756 | return [self extendedTraitsDelegate]; |
| 13757 | } |
| 13758 | |
| 13759 | #if ENABLE(REVEAL)(defined 1 && 1) |
| 13760 | |
| 13761 | - (void)selectTextForEditMenuWithLocationInView:(CGPoint)locationInView completionHandler:(void(^)(BOOL shouldPresentMenu, NSString * _Nullable contextString, NSRange selectedRangeInContextString))completionHandler |
| 13762 | { |
| 13763 | [self selectTextForContextMenuWithLocationInView:locationInView completionHandler:completionHandler]; |
| 13764 | } |
| 13765 | |
| 13766 | #endif // ENABLE(REVEAL) |
| 13767 | |
| 13768 | - (BOOL)isSelectionAtDocumentStart |
| 13769 | { |
| 13770 | return self.selectionAtDocumentStart; |
| 13771 | } |
| 13772 | |
| 13773 | - (BOOL)automaticallyPresentEditMenu |
| 13774 | { |
| 13775 | return _suppressSelectionAssistantReasons.isEmpty(); |
| 13776 | } |
| 13777 | |
| 13778 | - (id<BETextInputDelegate>)asyncInputDelegate |
| 13779 | { |
| 13780 | return _asyncInputDelegate; |
| 13781 | } |
| 13782 | |
| 13783 | - (void)setAsyncInputDelegate:(id<BETextInputDelegate>)delegate |
| 13784 | { |
| 13785 | _asyncInputDelegate = delegate; |
| 13786 | } |
| 13787 | |
| 13788 | - (void)handleKeyEntry:(BEKeyEntry *)event withCompletionHandler:(void (^)(BEKeyEntry *, BOOL))completionHandler |
| 13789 | { |
| 13790 | auto webEvent = adoptNS([[::WebEvent alloc] initWithKeyEntry:event]); |
| 13791 | [self _internalHandleKeyWebEvent:webEvent.get() withCompletionHandler:[originalEvent = retainPtr(event), completionHandler = makeBlockPtr(completionHandler)](::WebEvent *webEvent, BOOL handled) { |
| 13792 | ASSERT(webEvent.originalKeyEntry == originalEvent)((void)0); |
| 13793 | completionHandler(originalEvent.get(), handled); |
| 13794 | }]; |
| 13795 | } |
| 13796 | |
| 13797 | - (void)replaceText:(NSString *)originalText withText:(NSString *)replacementText options:(BETextReplacementOptions)options completionHandler:(void (^)(NSArray<UITextSelectionRect *> *rects))completionHandler |
| 13798 | { |
| 13799 | [self _internalReplaceText:originalText withText:replacementText isCandidate:options & BETextReplacementOptionsAddUnderline completion:[view = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)](bool wasReplaced) { |
| 13800 | if (!wasReplaced) |
| 13801 | return completionHandler(@[ ]); |
| 13802 | |
| 13803 | auto& data = view->_autocorrectionData; |
| 13804 | RetainPtr firstSelectionRect = [WKUITextSelectionRect selectionRectWithCGRect:data.textFirstRect]; |
| 13805 | if (CGRectEqualToRect(data.textFirstRect, data.textLastRect)) |
| 13806 | return completionHandler(@[ firstSelectionRect.get() ]); |
| 13807 | |
| 13808 | completionHandler(@[ firstSelectionRect.get(), [WKUITextSelectionRect selectionRectWithCGRect:data.textLastRect] ]); |
| 13809 | }]; |
| 13810 | } |
| 13811 | |
| 13812 | - (void)requestTextRectsForString:(NSString *)input withCompletionHandler:(void (^)(NSArray<UITextSelectionRect *> *rects))completionHandler |
| 13813 | { |
| 13814 | [self _internalRequestTextRectsForString:input completion:[view = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)](auto& rects) mutable { |
| 13815 | auto selectionRects = createNSArray(rects, [](auto& rect) { |
| 13816 | return [WKUITextSelectionRect selectionRectWithCGRect:rect]; |
| 13817 | }); |
| 13818 | completionHandler(selectionRects.get()); |
| 13819 | }]; |
| 13820 | } |
| 13821 | |
| 13822 | - (UIView *)textInputView |
| 13823 | { |
| 13824 | return self; |
| 13825 | } |
| 13826 | |
| 13827 | - (void)autoscrollToPoint:(CGPoint)pointInDocument |
| 13828 | { |
| 13829 | protect(_page)->startAutoscrollAtPosition(pointInDocument); |
| 13830 | } |
| 13831 | |
| 13832 | - (void)requestTextContextForAutocorrectionWithCompletionHandler:(void (^)(WKBETextDocumentContextBETextDocumentContext *context))completion |
| 13833 | { |
| 13834 | if (!completion) { |
| 13835 | [NSException raise:NSInvalidArgumentException format:@"Expected a nonnull completion handler in %s.", __PRETTY_FUNCTION__]; |
| 13836 | return; |
| 13837 | } |
| 13838 | |
| 13839 | [self _internalRequestAutocorrectionContextWithCompletionHandler:[completion = makeBlockPtr(completion), strongSelf = retainPtr(self)](WebKit::RequestAutocorrectionContextResult result) { |
| 13840 | WebKit::DocumentEditingContext context; |
| 13841 | switch (result) { |
| 13842 | case WebKit::RequestAutocorrectionContextResult::Empty: |
| 13843 | break; |
| 13844 | case WebKit::RequestAutocorrectionContextResult::LastContext: |
| 13845 | auto& webContext = strongSelf->_lastAutocorrectionContext; |
| 13846 | context.contextBefore.string = webContext.contextBefore; |
| 13847 | context.markedText.string = webContext.markedText; |
| 13848 | context.selectedText.string = webContext.selectedText; |
| 13849 | context.contextAfter.string = webContext.contextAfter; |
| 13850 | NSRange selectedRangeInMarkedText = webContext.selectedRangeInMarkedText; |
| 13851 | context.selectedRangeInMarkedText = { |
| 13852 | .location = selectedRangeInMarkedText.location, |
| 13853 | .length = selectedRangeInMarkedText.length |
| 13854 | }; |
| 13855 | break; |
| 13856 | } |
| 13857 | completion(protect(context.toPlatformContext({ WebKit::DocumentEditingContextRequest::Options::Text })).get()); |
| 13858 | }]; |
| 13859 | } |
| 13860 | |
| 13861 | #if ENABLE(REVEAL)(defined 1 && 1) |
| 13862 | |
| 13863 | - (void)selectTextForContextMenuWithLocationInView:(CGPoint)locationInView completionHandler:(void(^)(BOOL shouldPresentMenu, NSString *contextString, NSRange selectedRangeInContextString))completionHandler |
| 13864 | { |
| 13865 | [self _internalSelectTextForContextMenuWithLocationInView:locationInView completionHandler:[completionHandler = makeBlockPtr(completionHandler)](BOOL shouldPresentMenu, const WebKit::RevealItem& item) { |
| 13866 | auto& selectedRange = item.selectedRange(); |
| 13867 | completionHandler(shouldPresentMenu, item.text().createNSString().get(), NSMakeRange(selectedRange.location, selectedRange.length)); |
| 13868 | }]; |
| 13869 | } |
| 13870 | |
| 13871 | #endif // ENABLE(REVEAL) |
| 13872 | |
| 13873 | inline static NSArray<NSString *> *deleteSelectionCommands(UITextStorageDirection direction, UITextGranularity granularity) |
| 13874 | { |
| 13875 | BOOL backward = direction == UITextStorageDirectionBackward; |
| 13876 | switch (granularity) { |
| 13877 | case UITextGranularityCharacter: |
| 13878 | return @[ backward ? @"DeleteBackward" : @"DeleteForward" ]; |
| 13879 | case UITextGranularityWord: |
| 13880 | return @[ backward ? @"DeleteWordBackward" : @"DeleteWordForward" ]; |
| 13881 | case UITextGranularitySentence: |
| 13882 | return @[ backward ? @"MoveToBeginningOfSentenceAndModifySelection" : @"MoveToEndOfSentenceAndModifySelection", @"DeleteBackward" ]; |
| 13883 | case UITextGranularityParagraph: |
| 13884 | return @[ backward ? @"DeleteToBeginningOfParagraph" : @"DeleteToEndOfParagraph" ]; |
| 13885 | case UITextGranularityLine: |
| 13886 | return @[ backward ? @"DeleteToBeginningOfLine" : @"DeleteToEndOfLine" ]; |
| 13887 | case UITextGranularityDocument: |
| 13888 | return @[ backward ? @"MoveToBeginningOfDocumentAndModifySelection" : @"MoveToEndOfDocumentAndModifySelection", @"DeleteBackward" ]; |
| 13889 | } |
| 13890 | ASSERT_NOT_REACHED()((void)0); |
| 13891 | return @[ ]; |
| 13892 | } |
| 13893 | |
| 13894 | inline static NSArray<NSString *> *moveSelectionCommand(UITextStorageDirection direction, UITextGranularity granularity) |
| 13895 | { |
| 13896 | BOOL backward = direction == UITextStorageDirectionBackward; |
| 13897 | switch (granularity) { |
| 13898 | case UITextGranularityCharacter: |
| 13899 | return @[ backward ? @"MoveBackward" : @"MoveForward" ]; |
| 13900 | case UITextGranularityWord: |
| 13901 | return @[ backward ? @"MoveWordBackward" : @"MoveWordForward" ]; |
| 13902 | case UITextGranularitySentence: |
| 13903 | return @[ backward ? @"MoveToBeginningOfSentence" : @"MoveToEndOfSentence" ]; |
| 13904 | case UITextGranularityParagraph: |
| 13905 | return @[ |
| 13906 | backward ? @"MoveBackward" : @"MoveForward", |
| 13907 | backward ? @"MoveToBeginningOfParagraph" : @"MoveToEndOfParagraph" |
| 13908 | ]; |
| 13909 | case UITextGranularityLine: |
| 13910 | return @[ backward ? @"MoveToBeginningOfLine" : @"MoveToEndOfLine" ]; |
| 13911 | case UITextGranularityDocument: |
| 13912 | return @[ backward ? @"MoveToBeginningOfDocument" : @"MoveToEndOfDocument" ]; |
| 13913 | } |
| 13914 | ASSERT_NOT_REACHED()((void)0); |
| 13915 | return nilnullptr; |
| 13916 | } |
| 13917 | |
| 13918 | inline static NSArray<NSString *> *extendSelectionCommand(UITextStorageDirection direction, UITextGranularity granularity) |
| 13919 | { |
| 13920 | BOOL backward = direction == UITextStorageDirectionBackward; |
| 13921 | switch (granularity) { |
| 13922 | case UITextGranularityCharacter: |
| 13923 | return @[ backward ? @"MoveBackwardAndModifySelection" : @"MoveForwardAndModifySelection" ]; |
| 13924 | case UITextGranularityWord: |
| 13925 | return @[ backward ? @"MoveWordBackwardAndModifySelection" : @"MoveWordForwardAndModifySelection" ]; |
| 13926 | case UITextGranularitySentence: |
| 13927 | return @[ backward ? @"MoveToBeginningOfSentenceAndModifySelection" : @"MoveToEndOfSentenceAndModifySelection" ]; |
| 13928 | case UITextGranularityParagraph: |
| 13929 | return @[ |
| 13930 | backward ? @"MoveBackwardAndModifySelection" : @"MoveForwardAndModifySelection", |
| 13931 | backward ? @"MoveToBeginningOfParagraphAndModifySelection" : @"MoveToEndOfParagraphAndModifySelection" |
| 13932 | ]; |
| 13933 | case UITextGranularityLine: |
| 13934 | return @[ backward ? @"MoveToBeginningOfLineAndModifySelection" : @"MoveToEndOfLineAndModifySelection" ]; |
| 13935 | case UITextGranularityDocument: |
| 13936 | return @[ backward ? @"MoveToBeginningOfDocumentAndModifySelection" : @"MoveToEndOfDocumentAndModifySelection" ]; |
| 13937 | } |
| 13938 | ASSERT_NOT_REACHED()((void)0); |
| 13939 | return nilnullptr; |
| 13940 | } |
| 13941 | |
| 13942 | inline static NSString *moveSelectionCommand(UITextLayoutDirection direction) |
| 13943 | { |
| 13944 | switch (direction) { |
| 13945 | case UITextLayoutDirectionRight: |
| 13946 | return @"MoveRight"; |
| 13947 | case UITextLayoutDirectionLeft: |
| 13948 | return @"MoveLeft"; |
| 13949 | case UITextLayoutDirectionUp: |
| 13950 | return @"MoveUp"; |
| 13951 | case UITextLayoutDirectionDown: |
| 13952 | return @"MoveDown"; |
| 13953 | } |
| 13954 | ASSERT_NOT_REACHED()((void)0); |
| 13955 | return nilnullptr; |
| 13956 | } |
| 13957 | |
| 13958 | inline static NSString *extendSelectionCommand(UITextLayoutDirection direction) |
| 13959 | { |
| 13960 | switch (direction) { |
| 13961 | case UITextLayoutDirectionRight: |
| 13962 | return @"MoveRightAndModifySelection"; |
| 13963 | case UITextLayoutDirectionLeft: |
| 13964 | return @"MoveLeftAndModifySelection"; |
| 13965 | case UITextLayoutDirectionUp: |
| 13966 | return @"MoveUpAndModifySelection"; |
| 13967 | case UITextLayoutDirectionDown: |
| 13968 | return @"MoveDownAndModifySelection"; |
| 13969 | } |
| 13970 | ASSERT_NOT_REACHED()((void)0); |
| 13971 | return nilnullptr; |
| 13972 | } |
| 13973 | |
| 13974 | - (void)deleteInDirection:(UITextStorageDirection)direction toGranularity:(UITextGranularity)granularity |
| 13975 | { |
| 13976 | auto notifyDelegate = granularity == UITextGranularityCharacter && direction == UITextStorageDirectionBackward |
| 13977 | ? WebKit::NotifyInputDelegate::No |
| 13978 | : WebKit::NotifyInputDelegate::Yes; |
| 13979 | |
| 13980 | for (NSString *command in deleteSelectionCommands(direction, granularity)) |
| 13981 | [self _executeEditCommand:command notifyDelegate:notifyDelegate]; |
| 13982 | } |
| 13983 | |
| 13984 | - (void)moveInStorageDirection:(UITextStorageDirection)direction byGranularity:(UITextGranularity)granularity |
| 13985 | { |
| 13986 | for (NSString *command in moveSelectionCommand(direction, granularity)) |
| 13987 | [self _executeEditCommand:command]; |
| 13988 | } |
| 13989 | |
| 13990 | - (void)moveInLayoutDirection:(UITextLayoutDirection)direction |
| 13991 | { |
| 13992 | [self _executeEditCommand:protect(moveSelectionCommand(direction)).get()]; |
| 13993 | } |
| 13994 | |
| 13995 | - (void)extendInStorageDirection:(UITextStorageDirection)direction byGranularity:(UITextGranularity)granularity |
| 13996 | { |
| 13997 | for (NSString *command in extendSelectionCommand(direction, granularity)) |
| 13998 | [self _executeEditCommand:command]; |
| 13999 | } |
| 14000 | |
| 14001 | - (void)extendInLayoutDirection:(UITextLayoutDirection)direction |
| 14002 | { |
| 14003 | [self _executeEditCommand:protect(extendSelectionCommand(direction)).get()]; |
| 14004 | } |
| 14005 | |
| 14006 | - (void)adjustSelectionByRange:(BEDirectionalTextRange)range completionHandler:(void (^)(void))completionHandler |
| 14007 | { |
| 14008 | [self _internalAdjustSelectionWithOffset:range.offset lengthDelta:range.length completionHandler:completionHandler]; |
| 14009 | } |
| 14010 | |
| 14011 | - (id<BEExtendedTextInputTraits>)extendedTraitsDelegate |
| 14012 | { |
| 14013 | if (self._requiresLegacyTextInputTraits) |
| 14014 | return static_cast<id<BEExtendedTextInputTraits>>(self.textInputTraits); |
| 14015 | |
| 14016 | if (!_extendedTextInputTraits) |
| 14017 | _extendedTextInputTraits = adoptNS([WKExtendedTextInputTraits new]); |
| 14018 | |
| 14019 | if (!self._hasFocusedElement && !_isFocusingElementWithKeyboard) { |
| 14020 | [_extendedTextInputTraits restoreDefaultValues]; |
| 14021 | [_extendedTextInputTraits setSelectionColorsToMatchTintColor:[self _cascadeInteractionTintColor]]; |
| 14022 | } else if (!_isBlurringFocusedElement) |
| 14023 | [self _updateTextInputTraits:_extendedTextInputTraits.get()]; |
| 14024 | |
| 14025 | return _extendedTextInputTraits.get(); |
| 14026 | } |
| 14027 | |
| 14028 | #endif // USE(BROWSERENGINEKIT) |
| 14029 | |
| 14030 | - (void)_internalAdjustSelectionWithOffset:(NSInteger)offset lengthDelta:(NSInteger)lengthDelta completionHandler:(void (^)(void))completionHandler |
| 14031 | { |
| 14032 | protect(_page)->updateSelectionWithDelta(offset, lengthDelta, [capturedCompletionHandler = makeBlockPtr(completionHandler)] { |
| 14033 | capturedCompletionHandler(); |
| 14034 | }); |
| 14035 | } |
| 14036 | |
| 14037 | - (CGRect)selectionClipRect |
| 14038 | { |
| 14039 | Ref page = *_page; |
| 14040 | if (page->waitingForPostLayoutEditorStateUpdateAfterFocusingElement()) |
| 14041 | return _focusedElementInformation.interactionRect; |
| 14042 | |
| 14043 | if (page->editorState().hasVisualData() && !page->editorState().visualData->selectionClipRect.isEmpty()) |
| 14044 | return page->editorState().visualData->selectionClipRect; |
| 14045 | |
| 14046 | return CGRectNull; |
| 14047 | } |
| 14048 | |
| 14049 | - (UIView *)selectionContainerViewBelowText |
| 14050 | { |
| 14051 | return self._selectionContainerViewInternal; |
| 14052 | } |
| 14053 | |
| 14054 | - (UIView *)selectionContainerViewAboveText |
| 14055 | { |
| 14056 | return self._selectionContainerViewInternal; |
| 14057 | } |
| 14058 | |
| 14059 | - (void)transposeCharactersAroundSelection |
| 14060 | { |
| 14061 | [self _executeEditCommand:@"transpose"]; |
| 14062 | } |
| 14063 | |
| 14064 | - (BOOL)selectionAtDocumentStart |
| 14065 | { |
| 14066 | Ref page = *_page; |
| 14067 | if (!page->editorState().postLayoutData) |
| 14068 | return NO__objc_no; |
| 14069 | return !page->editorState().postLayoutData->characterBeforeSelection; |
| 14070 | } |
| 14071 | |
| 14072 | - (BOOL)shouldSuppressEditMenu |
| 14073 | { |
| 14074 | return !!_suppressSelectionAssistantReasons; |
| 14075 | } |
| 14076 | |
| 14077 | #pragma mark - WKTextAnimationSourceDelegate |
| 14078 | |
| 14079 | #if ENABLE(WRITING_TOOLS)(defined 1 && 1) |
| 14080 | - (void)targetedPreviewForID:(NSUUID *)uuid completionHandler:(void (^)(UITargetedPreview *))completionHandler |
| 14081 | { |
| 14082 | auto animationID = WTF::UUID::fromNSUUID(uuid); |
| 14083 | |
| 14084 | protect(_page)->getTextIndicatorForID(*animationID, [protectedSelf = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)] (auto&& textIndicator) { |
| 14085 | if (!textIndicator) { |
| 14086 | completionHandler(nilnullptr); |
| 14087 | return; |
| 14088 | } |
| 14089 | |
| 14090 | RetainPtr targetedPreview = [protectedSelf _createTargetedPreviewFromTextIndicator:WTF::move(textIndicator) previewContainer:[protectedSelf containerForContextMenuHintPreviews]]; |
| 14091 | completionHandler(targetedPreview.get()); |
| 14092 | }); |
| 14093 | } |
| 14094 | |
| 14095 | - (void)updateUnderlyingTextVisibilityForTextAnimationID:(NSUUID *)uuid visible:(BOOL)visible completionHandler:(void (^)(void))completionHandler |
| 14096 | { |
| 14097 | NSUUID *destinationUUID = _sourceAnimationIDtoDestinationAnimationID.get()[uuid]; |
| 14098 | auto textUUID = WTF::UUID::fromNSUUID(uuid); |
| 14099 | if (destinationUUID) |
| 14100 | textUUID = WTF::UUID::fromNSUUID(destinationUUID); |
| 14101 | |
| 14102 | protect(_page)->updateUnderlyingTextVisibilityForTextAnimationID(*textUUID, visible, [completionHandler = makeBlockPtr(completionHandler)] () { |
| 14103 | completionHandler(); |
| 14104 | }); |
| 14105 | } |
| 14106 | |
| 14107 | - (UIView *)containingViewForTextAnimationType |
| 14108 | { |
| 14109 | return self; |
| 14110 | } |
| 14111 | |
| 14112 | - (void)callCompletionHandlerForAnimationID:(NSUUID *)uuid |
| 14113 | { |
| 14114 | NSUUID *destinationUUID = _sourceAnimationIDtoDestinationAnimationID.get()[uuid]; |
| 14115 | |
| 14116 | if (!destinationUUID) |
| 14117 | return; |
| 14118 | |
| 14119 | auto animationUUID = WTF::UUID::fromNSUUID(destinationUUID); |
| 14120 | protect(_page)->callCompletionHandlerForAnimationID(*animationUUID, WebCore::TextAnimationRunMode::RunAnimation); |
| 14121 | } |
| 14122 | |
| 14123 | - (void)callCompletionHandlerForAnimationID:(NSUUID *)uuid completionHandler:(void (^)(UITargetedPreview * _Nullable))completionHandler |
| 14124 | { |
| 14125 | auto animationUUID = WTF::UUID::fromNSUUID(uuid); |
| 14126 | |
| 14127 | // Store this completion handler so that it can be called after the execution of the next |
| 14128 | // call to replace the text and eventually use this completion handler to pass the |
| 14129 | // text indicator to UIKit. |
| 14130 | protect(_page)->storeDestinationCompletionHandlerForAnimationID(*animationUUID, [protectedSelf = retainPtr(self), completionHandler = makeBlockPtr(completionHandler)](RefPtr<WebCore::TextIndicator>&& textIndicator) { |
| 14131 | if (!textIndicator) { |
| 14132 | completionHandler(nilnullptr); |
| 14133 | return; |
| 14134 | } |
| 14135 | |
| 14136 | RetainPtr targetedPreview = [protectedSelf _createTargetedPreviewFromTextIndicator:WTF::move(textIndicator) previewContainer:[protectedSelf containerForContextMenuHintPreviews]]; |
| 14137 | completionHandler(targetedPreview.get()); |
| 14138 | }); |
| 14139 | |
| 14140 | protect(_page)->callCompletionHandlerForAnimationID(*animationUUID, WebCore::TextAnimationRunMode::RunAnimation); |
| 14141 | } |
| 14142 | |
| 14143 | - (void)replacementEffectDidComplete |
| 14144 | { |
| 14145 | protect(_page)->didEndPartialIntelligenceTextAnimationImpl(); |
| 14146 | } |
| 14147 | |
| 14148 | #endif |
| 14149 | |
| 14150 | #pragma mark - BETextInteractionDelegate |
| 14151 | |
| 14152 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 14153 | |
| 14154 | - (void)systemWillChangeSelectionForInteraction:(BETextInteraction *)interaction |
| 14155 | { |
| 14156 | [self _updateInternalStateBeforeSelectionChange]; |
| 14157 | } |
| 14158 | |
| 14159 | - (void)systemDidChangeSelectionForInteraction:(BETextInteraction *)interaction |
| 14160 | { |
| 14161 | [self _updateInternalStateAfterSelectionChange]; |
| 14162 | } |
| 14163 | |
| 14164 | #endif // USE(BROWSERENGINEKIT) |
| 14165 | |
| 14166 | #if ENABLE(WRITING_TOOLS)(defined 1 && 1) |
| 14167 | |
| 14168 | - (void)willPresentWritingTools |
| 14169 | { |
| 14170 | _isPresentingWritingTools = YES__objc_yes; |
| 14171 | // FIXME (rdar://problem/136376688): Stop manually hiding the accessory view once UIKit fixes rdar://136304542. |
| 14172 | self.formAccessoryView.hidden = YES__objc_yes; |
| 14173 | [self reloadInputViews]; |
| 14174 | } |
| 14175 | |
| 14176 | - (void)didDismissWritingTools |
| 14177 | { |
| 14178 | _isPresentingWritingTools = NO__objc_no; |
| 14179 | // FIXME (rdar://problem/136376688): Stop manually unhiding the accessory view once UIKit fixes rdar://136304542. |
| 14180 | self.formAccessoryView.hidden = NO__objc_no; |
| 14181 | [self reloadInputViews]; |
| 14182 | } |
| 14183 | |
| 14184 | - (CocoaWritingToolsResultOptions)allowedWritingToolsResultOptions |
| 14185 | { |
| 14186 | return [_webView.get() allowedWritingToolsResultOptions]; |
| 14187 | } |
| 14188 | |
| 14189 | - (UIWritingToolsBehavior)writingToolsBehavior |
| 14190 | { |
| 14191 | return [_webView.get() writingToolsBehavior]; |
| 14192 | } |
| 14193 | |
| 14194 | - (void)willBeginWritingToolsSession:(WTSession *)session requestContexts:(void (^)(NSArray<WTContext *> *))completion |
| 14195 | { |
| 14196 | [_webView.get() willBeginWritingToolsSession:session requestContexts:completion]; |
| 14197 | } |
| 14198 | |
| 14199 | - (void)willBeginWritingToolsSession:(WTSession *)session forProofreadingReview:(BOOL)proofreadingReview requestContexts:(void (^)(NSArray<WTContext *> *))completion |
| 14200 | { |
| 14201 | // FIXME - Staging: rdar://170159369 |
| 14202 | [(id)_webView.get() willBeginWritingToolsSession:session forProofreadingReview:proofreadingReview requestContexts:completion]; |
| 14203 | } |
| 14204 | |
| 14205 | - (void)didBeginWritingToolsSession:(WTSession *)session contexts:(NSArray<WTContext *> *)contexts |
| 14206 | { |
| 14207 | [_webView.get() didBeginWritingToolsSession:session contexts:contexts]; |
| 14208 | } |
| 14209 | |
| 14210 | - (void)proofreadingSession:(WTSession *)session didReceiveSuggestions:(NSArray<WTTextSuggestion *> *)suggestions processedRange:(NSRange)range inContext:(WTContext *)context finished:(BOOL)finished |
| 14211 | { |
| 14212 | [_webView.get() proofreadingSession:session didReceiveSuggestions:suggestions processedRange:range inContext:context finished:finished]; |
| 14213 | } |
| 14214 | |
| 14215 | - (void)proofreadingSession:(WTSession *)session didUpdateState:(WTTextSuggestionState)state forSuggestionWithUUID:(NSUUID *)suggestionUUID inContext:(WTContext *)context |
| 14216 | { |
| 14217 | [_webView.get() proofreadingSession:session didUpdateState:state forSuggestionWithUUID:suggestionUUID inContext:context]; |
| 14218 | } |
| 14219 | |
| 14220 | - (void)didEndWritingToolsSession:(WTSession *)session accepted:(BOOL)accepted |
| 14221 | { |
| 14222 | [_webView.get() didEndWritingToolsSession:session accepted:accepted]; |
| 14223 | } |
| 14224 | |
| 14225 | - (void)compositionSession:(WTSession *)session didReceiveText:(NSAttributedString *)attributedText replacementRange:(NSRange)range inContext:(WTContext *)context finished:(BOOL)finished |
| 14226 | { |
| 14227 | [_webView.get() compositionSession:session didReceiveText:attributedText replacementRange:range inContext:context finished:finished]; |
| 14228 | } |
| 14229 | |
| 14230 | - (void)writingToolsSession:(WTSession *)session didReceiveAction:(WTAction)action |
| 14231 | { |
| 14232 | [_webView.get() writingToolsSession:session didReceiveAction:action]; |
| 14233 | } |
| 14234 | |
| 14235 | static inline WKTextAnimationType toWKTextAnimationType(WebCore::TextAnimationType style) |
| 14236 | { |
| 14237 | switch (style) { |
| 14238 | case WebCore::TextAnimationType::Initial: |
| 14239 | return WKTextAnimationTypeInitial; |
| 14240 | case WebCore::TextAnimationType::Source: |
| 14241 | return WKTextAnimationTypeSource; |
| 14242 | case WebCore::TextAnimationType::Final: |
| 14243 | return WKTextAnimationTypeFinal; |
| 14244 | } |
| 14245 | } |
| 14246 | |
| 14247 | - (void)addTextAnimationForAnimationID:(NSUUID *)uuid withData:(const WebCore::TextAnimationData&)data |
| 14248 | { |
| 14249 | if (!protect(_page->preferences())->textAnimationsEnabled()) |
| 14250 | return; |
| 14251 | |
| 14252 | if (data.style == WebCore::TextAnimationType::Final) |
| 14253 | [_sourceAnimationIDtoDestinationAnimationID setObject:uuid forKey:data.sourceAnimationUUID.value_or(WTF::UUID(WTF::UUID::emptyValue)).createNSUUID().get()]; |
| 14254 | |
| 14255 | if (!_textAnimationManager) |
| 14256 | _textAnimationManager = adoptNS([WebKit::allocWKTextAnimationManagerInstance() initWithDelegate:self]); |
| 14257 | |
| 14258 | [_textAnimationManager addTextAnimationForAnimationID:uuid withStyleType:toWKTextAnimationType(data.style)]; |
| 14259 | } |
| 14260 | |
| 14261 | - (void)removeTextAnimationForAnimationID:(NSUUID *)uuid |
| 14262 | { |
| 14263 | if (!uuid) |
| 14264 | return; |
| 14265 | |
| 14266 | if (!protect(_page->preferences())->textAnimationsEnabled()) |
| 14267 | return; |
| 14268 | |
| 14269 | if (!_textAnimationManager) |
| 14270 | return; |
| 14271 | |
| 14272 | [_textAnimationManager removeTextAnimationForAnimationID:uuid]; |
| 14273 | } |
| 14274 | |
| 14275 | #endif |
| 14276 | |
| 14277 | #if ENABLE(MULTI_REPRESENTATION_HEIC)(defined ENABLE_MULTI_REPRESENTATION_HEIC && ENABLE_MULTI_REPRESENTATION_HEIC ) |
| 14278 | |
| 14279 | - (BOOL)supportsAdaptiveImageGlyph |
| 14280 | { |
| 14281 | if (self.webView._isEditable || self.webView.configuration._multiRepresentationHEICInsertionEnabled) |
| 14282 | return _page->editorState().isContentRichlyEditable; |
| 14283 | |
| 14284 | return NO__objc_no; |
| 14285 | } |
| 14286 | |
| 14287 | - (void)insertAdaptiveImageGlyph:(NSAdaptiveImageGlyph *)adaptiveImageGlyph replacementRange:(UITextRange *)replacementRange |
| 14288 | { |
| 14289 | _page->insertMultiRepresentationHEIC(adaptiveImageGlyph.imageContent, adaptiveImageGlyph.contentDescription); |
| 14290 | } |
| 14291 | |
| 14292 | #endif |
| 14293 | |
| 14294 | - (void)_closeCurrentTypingCommand |
| 14295 | { |
| 14296 | if (!_page) |
| 14297 | return; |
| 14298 | |
| 14299 | protect(_page)->closeCurrentTypingCommand(); |
| 14300 | } |
| 14301 | |
| 14302 | - (UIView *)_selectionContainerViewAboveText |
| 14303 | { |
| 14304 | // FIXME: Consider adding ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED() |
| 14305 | // once the changes in rdar://150645383 are in all relevant builds. |
| 14306 | return self._selectionContainerViewInternal; |
| 14307 | } |
| 14308 | |
| 14309 | - (UIView *)selectionContainerView |
| 14310 | { |
| 14311 | // FIXME: Consider adding ASSERT_ASYNC_TEXT_INTERACTIONS_DISABLED() |
| 14312 | // once the changes in rdar://150645383 are in all relevant builds. |
| 14313 | return self._selectionContainerViewInternal; |
| 14314 | } |
| 14315 | |
| 14316 | - (UIView *)_selectionContainerViewInternal |
| 14317 | { |
| 14318 | if (_cachedSelectionContainerView) |
| 14319 | return _cachedSelectionContainerView; |
| 14320 | |
| 14321 | _cachedSelectionContainerView = [&] -> UIView * { |
| 14322 | if (!self.selectionHonorsOverflowScrolling) |
| 14323 | return self; |
| 14324 | |
| 14325 | Ref page = *_page; |
| 14326 | if (!page->editorState().hasVisualData()) |
| 14327 | return self; |
| 14328 | |
| 14329 | RetainPtr enclosingView = [self _viewForLayerID:page->editorState().visualData->enclosingLayerID]; |
| 14330 | if (!enclosingView) |
| 14331 | return self; |
| 14332 | |
| 14333 | for (UIView *selectedView in self.allViewsIntersectingSelectionRange) { |
| 14334 | if (enclosingView != selectedView && ![enclosingView _wk_isAncestorOf:selectedView]) |
| 14335 | return self; |
| 14336 | } |
| 14337 | return enclosingView.autorelease(); |
| 14338 | }(); |
| 14339 | |
| 14340 | ASSERT(_cachedSelectionContainerView)((void)0); |
| 14341 | return _cachedSelectionContainerView; |
| 14342 | } |
| 14343 | |
| 14344 | - (UIView *)_viewForLayerID:(std::optional<WebCore::PlatformLayerIdentifier>)layerID |
| 14345 | { |
| 14346 | WeakPtr drawingArea = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(_page->drawingArea()); |
| 14347 | if (!drawingArea) |
| 14348 | return nilnullptr; |
| 14349 | |
| 14350 | WeakPtr layerTreeNode = drawingArea->remoteLayerTreeHost().nodeForID(layerID); |
| 14351 | if (!layerTreeNode) |
| 14352 | return nilnullptr; |
| 14353 | |
| 14354 | return layerTreeNode->uiView(); |
| 14355 | } |
| 14356 | |
| 14357 | - (NSArray<UIView *> *)allViewsIntersectingSelectionRange |
| 14358 | { |
| 14359 | if (!self.selectionHonorsOverflowScrolling) |
| 14360 | return @[ ]; |
| 14361 | |
| 14362 | if (!protect(_page)->editorState().hasVisualData()) |
| 14363 | return @[ ]; |
| 14364 | |
| 14365 | return createNSArray(protect(_page)->editorState().visualData->intersectingLayerIDs, [&](auto& layerID) { |
| 14366 | return [self _viewForLayerID:layerID]; |
| 14367 | }).autorelease(); |
| 14368 | } |
| 14369 | |
| 14370 | - (BOOL)_shouldHideSelectionDuringOverflowScroll:(UIScrollView *)scrollView |
| 14371 | { |
| 14372 | if (self._selectionContainerViewInternal == self) |
| 14373 | return YES__objc_yes; |
| 14374 | |
| 14375 | auto& state = protect(_page)->editorState(); |
| 14376 | if (!state.hasVisualData()) |
| 14377 | return NO__objc_no; |
| 14378 | |
| 14379 | auto enclosingScrollingNodeID = state.visualData->enclosingScrollingNodeID; |
| 14380 | RetainPtr enclosingScroller = [self _scrollViewForScrollingNodeID:enclosingScrollingNodeID] ?: self._scroller; |
| 14381 | if (!enclosingScroller || ![enclosingScroller _wk_isAncestorOf:scrollView]) |
| 14382 | return NO__objc_no; |
| 14383 | |
| 14384 | auto isInSeparateScrollView = [&](std::optional<WebCore::ScrollingNodeID> endpointScrollingNodeID) -> bool { |
| 14385 | if (endpointScrollingNodeID == enclosingScrollingNodeID) |
| 14386 | return false; |
| 14387 | |
| 14388 | RetainPtr endpointScroller = [self _scrollViewForScrollingNodeID:endpointScrollingNodeID] ?: self._scroller; |
| 14389 | return endpointScroller == scrollView || [scrollView _wk_isAncestorOf:endpointScroller.get()]; |
| 14390 | }; |
| 14391 | |
| 14392 | if (isInSeparateScrollView(state.visualData->scrollingNodeIDAtStart)) |
| 14393 | return YES__objc_yes; |
| 14394 | |
| 14395 | if (isInSeparateScrollView(state.visualData->scrollingNodeIDAtEnd)) |
| 14396 | return YES__objc_yes; |
| 14397 | |
| 14398 | return NO__objc_no; |
| 14399 | } |
| 14400 | |
| 14401 | - (BOOL)_canStartNavigationSwipeAtLastInteractionLocation |
| 14402 | { |
| 14403 | RetainPtr lastHitView = [self hitTest:_lastInteractionLocation withEvent:nilnullptr]; |
| 14404 | if (!lastHitView) |
| 14405 | return YES__objc_yes; |
| 14406 | |
| 14407 | RetainPtr mainScroller = [_webView.get() scrollView]; |
| 14408 | RetainPtr innerScroller = dynamic_objc_cast<UIScrollView>(lastHitView.get()) ?: [lastHitView _wk_parentScrollView]; |
| 14409 | for (RetainPtr scroller = innerScroller; scroller; scroller = [scroller _wk_parentScrollView]) { |
| 14410 | if ([scroller isDragging]) |
| 14411 | return NO__objc_no; |
| 14412 | |
| 14413 | if ([scroller panGestureRecognizer]._wk_hasRecognizedOrEnded) |
| 14414 | return NO__objc_no; |
| 14415 | |
| 14416 | if (scroller == mainScroller) |
| 14417 | break; |
| 14418 | } |
| 14419 | |
| 14420 | auto touchActions = WebKit::touchActionsForPoint(self, WebCore::roundedIntPoint(_lastInteractionLocation)); |
| 14421 | using enum WebCore::TouchAction; |
| 14422 | return touchActions.containsAny({ Auto, PanX, Manipulation }); |
| 14423 | } |
| 14424 | |
| 14425 | - (BOOL)allowsTouchPanningAtPoint:(CGPoint)point |
| 14426 | { |
| 14427 | using enum WebCore::TouchAction; |
| 14428 | auto touchActions = WebKit::touchActionsForPoint(self, WebCore::roundedIntPoint(point)); |
| 14429 | return touchActions.containsAny({ Auto, PanX, PanY, Manipulation }); |
| 14430 | } |
| 14431 | |
| 14432 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 14433 | - (void)_willInvalidateDraggedModelWithContainerView:(UIView *)containerView |
| 14434 | { |
| 14435 | // The model is being removed while it's in the middle of a drag. |
| 14436 | // We need to make sure it stays in a window by adding it to the |
| 14437 | // WKContentView's container for drag previews, but keep it hidden. |
| 14438 | [containerView setFrame:CGRectZero]; |
| 14439 | containerView.hidden = YES__objc_yes; |
| 14440 | [self.containerForDragPreviews addSubview:containerView]; |
| 14441 | } |
| 14442 | #endif |
| 14443 | |
| 14444 | #if HAVE(UI_CONVERSATION_CONTEXT)(defined 1 && 1) |
| 14445 | |
| 14446 | - (UIConversationContext *)_conversationContext |
| 14447 | { |
| 14448 | if (self._requiresLegacyTextInputTraits) |
| 14449 | return self.textInputTraits.conversationContext; |
| 14450 | |
| 14451 | return self.extendedTraitsDelegate.conversationContext; |
| 14452 | } |
| 14453 | |
| 14454 | - (void)_setConversationContext:(UIConversationContext *)context |
| 14455 | { |
| 14456 | [_legacyTextInputTraits setConversationContext:context]; |
| 14457 | [_extendedTextInputTraits setConversationContext:context]; |
| 14458 | |
| 14459 | [self.inputDelegate conversationContext:context didChange:self]; |
| 14460 | } |
| 14461 | |
| 14462 | - (void)insertInputSuggestion:(UIInputSuggestion *)suggestion |
| 14463 | { |
| 14464 | RetainPtr webView = _webView.get(); |
| 14465 | RetainPtr delegate = [webView UIDelegate]; |
| 14466 | |
| 14467 | if (![delegate respondsToSelector:@selector(webView:insertInputSuggestion:)]) { |
| 14468 | RELEASE_LOG_ERROR(TextInput, "Ignored input suggestion (UI delegate does not implement -webView:insertInputSuggestion:)")clang diagnostic push
clang diagnostic ignored "-Wunknown-warning-option" clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang ::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogTextInput .osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({ clang diagnostic push clang diagnostic ignored "-Wvla" clang diagnostic error "-Wformat" _Static_assert(__builtin_constant_p ("Ignored input suggestion (UI delegate does not implement -webView:insertInputSuggestion:)" ), "format/label/description argument must be a string constant" ); __attribute__((section("__TEXT,__oslogstring,cstring_literals" ),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG272" ) = "Ignored input suggestion (UI delegate does not implement -webView:insertInputSuggestion:)" ; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf [__builtin_os_log_format_buffer_size("Ignored input suggestion (UI delegate does not implement -webView:insertInputSuggestion:)" )]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp , _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf , "Ignored input suggestion (UI delegate does not implement -webView:insertInputSuggestion:)" ), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic pop ; |
| 14469 | return; |
| 14470 | } |
| 14471 | |
| 14472 | [delegate webView:webView.get() insertInputSuggestion:suggestion]; |
| 14473 | } |
| 14474 | |
| 14475 | #endif // HAVE(UI_CONVERSATION_CONTEXT) |
| 14476 | |
| 14477 | @end |
| 14478 | |
| 14479 | #if ENABLE(POINTER_LOCK)(defined 1 && 1) |
| 14480 | |
| 14481 | @implementation WKContentView (PointerLock) |
| 14482 | |
| 14483 | - (void)_beginPointerLockMouseTracking |
| 14484 | { |
| 14485 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 14486 | [_mouseInteraction beginPointerLockMouseTracking]; |
| 14487 | #endif |
| 14488 | } |
| 14489 | |
| 14490 | - (void)_endPointerLockMouseTracking |
| 14491 | { |
| 14492 | #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)(defined 1 && 1) |
| 14493 | [_mouseInteraction endPointerLockMouseTracking]; |
| 14494 | #endif |
| 14495 | } |
| 14496 | |
| 14497 | @end |
| 14498 | |
| 14499 | #endif // ENABLE(POINTER_LOCK) |
| 14500 | |
| 14501 | @implementation WKContentView (WKTesting) |
| 14502 | |
| 14503 | - (void)selectWordBackwardForTesting |
| 14504 | { |
| 14505 | _autocorrectionContextNeedsUpdate = YES__objc_yes; |
| 14506 | protect(_page)->selectWordBackward(); |
| 14507 | } |
| 14508 | |
| 14509 | - (void)_doAfterReceivingEditDragSnapshotForTesting:(dispatch_block_t)action |
| 14510 | { |
| 14511 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 14512 | ASSERT(!_actionToPerformAfterReceivingEditDragSnapshot)((void)0); |
| 14513 | if (_waitingForEditDragSnapshot) { |
| 14514 | _actionToPerformAfterReceivingEditDragSnapshot = action; |
| 14515 | return; |
| 14516 | } |
| 14517 | #endif |
| 14518 | action(); |
| 14519 | } |
| 14520 | |
| 14521 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 14522 | - (WKDateTimeInputControl *)dateTimeInputControl |
| 14523 | { |
| 14524 | if ([_inputPeripheral isKindOfClass:WKDateTimeInputControl.class]) |
| 14525 | return (WKDateTimeInputControl *)_inputPeripheral.get(); |
| 14526 | return nilnullptr; |
| 14527 | } |
| 14528 | #endif |
| 14529 | |
| 14530 | - (WKFormSelectControl *)selectControl |
| 14531 | { |
| 14532 | if ([_inputPeripheral isKindOfClass:WKFormSelectControl.class]) |
| 14533 | return (WKFormSelectControl *)_inputPeripheral.get(); |
| 14534 | return nilnullptr; |
| 14535 | } |
| 14536 | |
| 14537 | #if ENABLE(DRAG_SUPPORT)(defined 1 && 1) |
| 14538 | |
| 14539 | - (BOOL)isAnimatingDragCancel |
| 14540 | { |
| 14541 | return _isAnimatingDragCancel; |
| 14542 | } |
| 14543 | |
| 14544 | #endif // ENABLE(DRAG_SUPPORT) |
| 14545 | |
| 14546 | - (void)_simulateTextEntered:(NSString *)text |
| 14547 | { |
| 14548 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 14549 | if ([_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]]) { |
| 14550 | [(WKTextInputListViewController *)_presentedFullScreenInputViewController.get() enterText:text]; |
| 14551 | return; |
| 14552 | } |
| 14553 | |
| 14554 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 14555 | if (_presentedQuickboardController) { |
| 14556 | id <PUICQuickboardControllerDelegate> delegate = [_presentedQuickboardController delegate]; |
| 14557 | ASSERT(delegate == self)((void)0); |
| 14558 | auto string = adoptNS([[NSAttributedString alloc] initWithString:text]); |
| 14559 | [delegate quickboardController:_presentedQuickboardController.get() textInputValueDidChange:string.get()]; |
| 14560 | return; |
| 14561 | } |
| 14562 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 14563 | #endif // HAVE(PEPPER_UI_CORE) |
| 14564 | |
| 14565 | [self insertText:text]; |
| 14566 | } |
| 14567 | |
| 14568 | - (void)_simulateElementAction:(_WKElementActionType)actionType atLocation:(CGPoint)location |
| 14569 | { |
| 14570 | _layerTreeTransactionIdAtLastInteractionStart = protect(downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()))->lastCommittedMainFrameLayerTreeTransactionID(); |
| 14571 | [self doAfterPositionInformationUpdate:[actionType, self, protectedSelf = retainPtr(self)] (WebKit::InteractionInformationAtPosition info) { |
| 14572 | _WKActivatedElementInfo *elementInfo = [_WKActivatedElementInfo activatedElementInfoWithInteractionInformationAtPosition:info userInfo:nilnullptr]; |
| 14573 | _WKElementAction *action = [_WKElementAction _elementActionWithType:actionType info:elementInfo assistant:_actionSheetAssistant.get()]; |
| 14574 | [action runActionWithElementInfo:elementInfo]; |
| 14575 | } forRequest:WebKit::InteractionInformationRequest(WebCore::roundedIntPoint(location))]; |
| 14576 | } |
| 14577 | |
| 14578 | - (void)_simulateLongPressActionAtLocation:(CGPoint)location |
| 14579 | { |
| 14580 | RetainPtr<WKContentView> protectedSelf = self; |
| 14581 | [self doAfterPositionInformationUpdate:[protectedSelf] (WebKit::InteractionInformationAtPosition) { |
| 14582 | if (SEL action = [protectedSelf _actionForLongPress]) |
| 14583 | [protectedSelf performSelector:action]; |
| 14584 | } forRequest:WebKit::InteractionInformationRequest(WebCore::roundedIntPoint(location))]; |
| 14585 | } |
| 14586 | |
| 14587 | - (void)selectFormAccessoryPickerRow:(NSInteger)rowIndex |
| 14588 | { |
| 14589 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 14590 | if ([_presentedFullScreenInputViewController isKindOfClass:[WKSelectMenuListViewController class]]) |
| 14591 | [(WKSelectMenuListViewController *)_presentedFullScreenInputViewController.get() selectItemAtIndex:rowIndex]; |
| 14592 | #else |
| 14593 | if ([_inputPeripheral isKindOfClass:[WKFormSelectControl class]]) |
| 14594 | [(WKFormSelectControl *)_inputPeripheral selectRow:rowIndex inComponent:0 extendingSelection:NO__objc_no]; |
| 14595 | #endif |
| 14596 | } |
| 14597 | |
| 14598 | - (BOOL)selectFormAccessoryHasCheckedItemAtRow:(long)rowIndex |
| 14599 | { |
| 14600 | #if !HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 14601 | if ([_inputPeripheral isKindOfClass:[WKFormSelectControl self]]) |
| 14602 | return [(WKFormSelectControl *)_inputPeripheral selectFormAccessoryHasCheckedItemAtRow:rowIndex]; |
| 14603 | #endif |
| 14604 | return NO__objc_no; |
| 14605 | } |
| 14606 | |
| 14607 | - (void)setSelectedColorForColorPicker:(UIColor *)color |
| 14608 | { |
| 14609 | if ([_inputPeripheral isKindOfClass:[WKFormColorControl class]]) |
| 14610 | [(WKFormColorControl *)_inputPeripheral selectColor:color]; |
| 14611 | } |
| 14612 | |
| 14613 | - (NSString *)textContentTypeForTesting |
| 14614 | { |
| 14615 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 14616 | if ([_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]]) |
| 14617 | return [(WKTextInputListViewController *)_presentedFullScreenInputViewController textInputContext].textContentType; |
| 14618 | #if HAVE(QUICKBOARD_CONTROLLER)(defined HAVE_QUICKBOARD_CONTROLLER && HAVE_QUICKBOARD_CONTROLLER ) |
| 14619 | if (_presentedQuickboardController) |
| 14620 | return [_presentedQuickboardController textInputContext].textContentType; |
| 14621 | #endif // HAVE(QUICKBOARD_CONTROLLER) |
| 14622 | #endif // HAVE(PEPPER_UI_CORE) |
| 14623 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 14624 | if (!self._requiresLegacyTextInputTraits) |
| 14625 | return self.extendedTraitsDelegate.textContentType; |
| 14626 | #endif |
| 14627 | return self.textInputTraits.textContentType; |
| 14628 | } |
| 14629 | |
| 14630 | - (NSString *)selectFormPopoverTitle |
| 14631 | { |
| 14632 | if (![_inputPeripheral isKindOfClass:[WKFormSelectControl class]]) |
| 14633 | return nilnullptr; |
| 14634 | |
| 14635 | return [(WKFormSelectControl *)_inputPeripheral selectFormPopoverTitle]; |
| 14636 | } |
| 14637 | |
| 14638 | - (NSString *)formInputLabel |
| 14639 | { |
| 14640 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 14641 | return [self inputLabelTextForViewController:_presentedFullScreenInputViewController.get()]; |
| 14642 | #else |
| 14643 | return nilnullptr; |
| 14644 | #endif |
| 14645 | } |
| 14646 | |
| 14647 | - (void)setTimePickerValueToHour:(NSInteger)hour minute:(NSInteger)minute |
| 14648 | { |
| 14649 | #if HAVE(PEPPER_UI_CORE)(defined HAVE_PEPPER_UI_CORE && HAVE_PEPPER_UI_CORE) |
| 14650 | if ([_presentedFullScreenInputViewController isKindOfClass:[WKTimePickerViewController class]]) |
| 14651 | [(WKTimePickerViewController *)_presentedFullScreenInputViewController.get() setHour:hour minute:minute]; |
| 14652 | #else |
| 14653 | if ([_inputPeripheral isKindOfClass:[WKDateTimeInputControl class]]) |
| 14654 | [(WKDateTimeInputControl *)_inputPeripheral.get() setTimePickerHour:hour minute:minute]; |
| 14655 | #endif |
| 14656 | } |
| 14657 | |
| 14658 | - (double)timePickerValueHour |
| 14659 | { |
| 14660 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 14661 | if ([_inputPeripheral isKindOfClass:[WKDateTimeInputControl class]]) |
| 14662 | return [(WKDateTimeInputControl *)_inputPeripheral.get() timePickerValueHour]; |
| 14663 | #endif |
| 14664 | return -1; |
| 14665 | } |
| 14666 | |
| 14667 | - (double)timePickerValueMinute |
| 14668 | { |
| 14669 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 14670 | if ([_inputPeripheral isKindOfClass:[WKDateTimeInputControl class]]) |
| 14671 | return [(WKDateTimeInputControl *)_inputPeripheral.get() timePickerValueMinute]; |
| 14672 | #endif |
| 14673 | return -1; |
| 14674 | } |
| 14675 | |
| 14676 | - (NSDictionary *)_contentsOfUserInterfaceItem:(NSString *)userInterfaceItem |
| 14677 | { |
| 14678 | if ([userInterfaceItem isEqualToString:@"actionSheet"]) |
| 14679 | return @{ userInterfaceItem: [protect(_actionSheetAssistant) currentlyAvailableActionTitles] }; |
| 14680 | |
| 14681 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) |
| 14682 | if ([userInterfaceItem isEqualToString:@"contextMenu"]) { |
| 14683 | auto itemTitles = adoptNS([NSMutableArray<NSString *> new]); |
| 14684 | [self.contextMenuInteraction updateVisibleMenuWithBlock:[&itemTitles](UIMenu *menu) -> UIMenu * { |
| 14685 | for (UIMenuElement *child in menu.children) |
| 14686 | [itemTitles addObject:child.title]; |
| 14687 | return menu; |
| 14688 | }]; |
| 14689 | if (self._shouldUseContextMenus) { |
| 14690 | return @{ userInterfaceItem: @{ |
| 14691 | @"url": _positionInformation.url.isValid() ? WTF::userVisibleString(_positionInformation.url.createNSURL().get()) : @"", |
| 14692 | @"isLink": [NSNumber numberWithBool:_positionInformation.isLink], |
| 14693 | @"isImage": [NSNumber numberWithBool:_positionInformation.isImage], |
| 14694 | @"imageURL": _positionInformation.imageURL.isValid() ? WTF::userVisibleString(_positionInformation.imageURL.createNSURL().get()) : @"", |
| 14695 | @"items": itemTitles.get() |
| 14696 | } }; |
| 14697 | } |
| 14698 | NSString *url = [_previewItemController previewData][UIPreviewDataLink]; |
| 14699 | return @{ userInterfaceItem: @{ |
| 14700 | @"url": url, |
| 14701 | @"isLink": [NSNumber numberWithBool:_positionInformation.isLink], |
| 14702 | @"isImage": [NSNumber numberWithBool:_positionInformation.isImage], |
| 14703 | @"imageURL": _positionInformation.imageURL.isValid() ? WTF::userVisibleString(_positionInformation.imageURL.createNSURL().get()) : @"", |
| 14704 | @"items": itemTitles.get() |
| 14705 | } }; |
| 14706 | } |
| 14707 | #endif |
| 14708 | |
| 14709 | #if ENABLE(MEDIA_CONTROLS_CONTEXT_MENUS)(defined 1 && 1) && USE(UICONTEXTMENU)(defined 1 && 1) |
| 14710 | if ([userInterfaceItem isEqualToString:@"mediaControlsContextMenu"]) |
| 14711 | return @{ userInterfaceItem: [protect(_actionSheetAssistant) currentlyAvailableMediaControlsContextMenuItems] }; |
| 14712 | #endif // ENABLE(MEDIA_CONTROLS_CONTEXT_MENUS) && USE(UICONTEXTMENU) |
| 14713 | |
| 14714 | if ([userInterfaceItem isEqualToString:@"fileUploadPanelMenu"]) { |
| 14715 | if (!_fileUploadPanel) |
| 14716 | return @{ userInterfaceItem: @[] }; |
| 14717 | return @{ userInterfaceItem: [_fileUploadPanel currentAvailableActionTitles] }; |
| 14718 | } |
| 14719 | |
| 14720 | if ([userInterfaceItem isEqualToString:@"selectMenu"]) { |
| 14721 | if (auto *menuItemTitles = [self.selectControl menuItemTitles]) |
| 14722 | return @{ userInterfaceItem: menuItemTitles }; |
| 14723 | return @{ userInterfaceItem: @[] }; |
| 14724 | } |
| 14725 | |
| 14726 | return nilnullptr; |
| 14727 | } |
| 14728 | |
| 14729 | - (void)_dismissContactPickerWithContacts:(NSArray *)contacts |
| 14730 | { |
| 14731 | #if HAVE(CONTACTSUI)(defined 1 && 1) |
| 14732 | [_contactPicker dismissWithContacts:contacts]; |
| 14733 | #endif |
| 14734 | } |
| 14735 | |
| 14736 | #if ENABLE(MODEL_PROCESS)(defined 0 && 0) |
| 14737 | - (void)_simulateModelInteractionPanGestureBeginAtPoint:(CGPoint)hitPoint |
| 14738 | { |
| 14739 | [self modelInteractionPanGestureDidBeginAtPoint:hitPoint]; |
| 14740 | } |
| 14741 | |
| 14742 | - (void)_simulateModelInteractionPanGestureUpdateAtPoint:(CGPoint)hitPoint |
| 14743 | { |
| 14744 | [self modelInteractionPanGestureDidUpdateWithPoint:hitPoint]; |
| 14745 | } |
| 14746 | |
| 14747 | - (NSDictionary *)_stageModeInfoForTesting |
| 14748 | { |
| 14749 | if (!_stageModeSession) |
| 14750 | return @{ }; |
| 14751 | |
| 14752 | return @{ |
| 14753 | @"awaitingResult" : @(_stageModeSession->isPreparingForInteraction), |
| 14754 | @"hitTestSuccessful" : @(_stageModeSession->nodeID.has_value()), |
| 14755 | }; |
| 14756 | } |
| 14757 | #endif |
| 14758 | |
| 14759 | - (UITapGestureRecognizer *)singleTapGestureRecognizer |
| 14760 | { |
| 14761 | return _singleTapGestureRecognizer.get(); |
| 14762 | } |
| 14763 | |
| 14764 | - (void)_simulateSelectionStart |
| 14765 | { |
| 14766 | _usingGestureForSelection = YES__objc_yes; |
| 14767 | _lastSelectionDrawingInfo.type = WebCore::SelectionType::Range; |
| 14768 | } |
| 14769 | |
| 14770 | #if ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)(defined 1 && 1) |
| 14771 | - (BOOL)_allowAnimationControls |
| 14772 | { |
| 14773 | return self.webView._allowAnimationControls; |
| 14774 | } |
| 14775 | #endif |
| 14776 | |
| 14777 | - (void)dismissFormAccessoryView |
| 14778 | { |
| 14779 | #if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS ) |
| 14780 | if (auto dateInput = self.dateTimeInputControl; [dateInput dismissWithAnimationForTesting]) |
| 14781 | return; |
| 14782 | #endif |
| 14783 | [self accessoryDone]; |
| 14784 | } |
| 14785 | |
| 14786 | - (void)_selectDataListOption:(NSInteger)optionIndex |
| 14787 | { |
| 14788 | [_dataListSuggestionsControl.get() didSelectOptionAtIndex:optionIndex]; |
| 14789 | } |
| 14790 | |
| 14791 | - (void)_setDataListSuggestionsControl:(WKDataListSuggestionsControl *)control |
| 14792 | { |
| 14793 | _dataListSuggestionsControl = control; |
| 14794 | } |
| 14795 | |
| 14796 | - (BOOL)isShowingDataListSuggestions |
| 14797 | { |
| 14798 | return [_dataListSuggestionsControl.get() isShowingSuggestions]; |
| 14799 | } |
| 14800 | |
| 14801 | - (UIWKTextInteractionAssistant *)textInteractionAssistant |
| 14802 | { |
| 14803 | return [_textInteractionWrapper textInteractionAssistant]; |
| 14804 | } |
| 14805 | |
| 14806 | @end |
| 14807 | |
| 14808 | #if HAVE(LINK_PREVIEW)(defined 1 && 1) |
| 14809 | |
| 14810 | static NSString *previewIdentifierForElementAction(_WKElementAction *action) |
| 14811 | { |
| 14812 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 14813 | switch (action.type) { |
| 14814 | case _WKElementActionTypeOpen: |
| 14815 | return WKPreviewActionItemIdentifierOpen; |
| 14816 | case _WKElementActionTypeCopy: |
| 14817 | return WKPreviewActionItemIdentifierCopy; |
| 14818 | #if !defined(TARGET_OS_IOS1) || TARGET_OS_IOS1 |
| 14819 | case _WKElementActionTypeAddToReadingList: |
| 14820 | return WKPreviewActionItemIdentifierAddToReadingList; |
| 14821 | #endif |
| 14822 | case _WKElementActionTypeShare: |
| 14823 | return WKPreviewActionItemIdentifierShare; |
| 14824 | default: |
| 14825 | return nilnullptr; |
| 14826 | } |
| 14827 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 14828 | ASSERT_NOT_REACHED()((void)0); |
| 14829 | return nilnullptr; |
| 14830 | } |
| 14831 | |
| 14832 | @implementation WKContentView (WKInteractionPreview) |
| 14833 | |
| 14834 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 14835 | - (UIContextMenuInteraction *)contextMenuInteraction |
| 14836 | { |
| 14837 | return [_textInteractionWrapper contextMenuInteraction]; |
| 14838 | } |
| 14839 | #endif // USE(UICONTEXTMENU) |
| 14840 | |
| 14841 | - (void)_registerPreview |
| 14842 | { |
| 14843 | if (!self.webView.allowsLinkPreview) |
| 14844 | return; |
| 14845 | |
| 14846 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 14847 | if (self._shouldUseContextMenus) { |
| 14848 | _contextMenuHasRequestedLegacyData = NO__objc_no; |
| 14849 | [_textInteractionWrapper setExternalContextMenuInteractionDelegate:self]; |
| 14850 | |
| 14851 | if (id<_UIClickInteractionDriving> driver = self.webView.configuration._clickInteractionDriverForTesting) |
| 14852 | [self.contextMenuInteraction presentationInteraction].overrideDrivers = @[driver]; |
| 14853 | return; |
| 14854 | } |
| 14855 | #endif // USE(UICONTEXTMENU) |
| 14856 | |
| 14857 | _previewItemController = adoptNS([[UIPreviewItemController alloc] initWithView:self]); |
| 14858 | [_previewItemController setDelegate:self]; |
| 14859 | _previewGestureRecognizer = _previewItemController.get().presentationGestureRecognizer; |
| 14860 | if ([_previewItemController respondsToSelector:@selector(presentationSecondaryGestureRecognizer)]) |
| 14861 | _previewSecondaryGestureRecognizer = _previewItemController.get().presentationSecondaryGestureRecognizer; |
| 14862 | } |
| 14863 | |
| 14864 | - (void)_unregisterPreview |
| 14865 | { |
| 14866 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 14867 | if (self._shouldUseContextMenus) |
| 14868 | return; |
| 14869 | #endif |
| 14870 | |
| 14871 | [_previewItemController setDelegate:nilnullptr]; |
| 14872 | _previewGestureRecognizer = nilnullptr; |
| 14873 | _previewSecondaryGestureRecognizer = nilnullptr; |
| 14874 | _previewItemController = nilnullptr; |
| 14875 | } |
| 14876 | |
| 14877 | #if USE(UICONTEXTMENU)(defined 1 && 1) |
| 14878 | |
| 14879 | static bool needsDeprecatedPreviewAPI(id<WKUIDelegate> delegate) |
| 14880 | { |
| 14881 | // FIXME: Replace these with booleans in UIDelegate.h. |
| 14882 | // Note that we explicitly do not test for @selector(_webView:contextMenuDidEndForElement:) |
| 14883 | // and @selector(webView:contextMenuWillPresentForElement) since the methods are used by MobileSafari |
| 14884 | // to manage state despite the app not moving to the new API. |
| 14885 | |
| 14886 | return delegate |
| 14887 | && ![delegate respondsToSelector:@selector(_webView:contextMenuConfigurationForElement:completionHandler:)] |
| 14888 | && ![delegate respondsToSelector:@selector(webView:contextMenuConfigurationForElement:completionHandler:)] |
| 14889 | && ![delegate respondsToSelector:@selector(_webView:contextMenuForElement:willCommitWithAnimator:)] |
| 14890 | && ![delegate respondsToSelector:@selector(webView:contextMenuForElement:willCommitWithAnimator:)] |
| 14891 | && ![delegate respondsToSelector:@selector(_webView:contextMenuWillPresentForElement:)] |
| 14892 | && ![delegate respondsToSelector:@selector(webView:contextMenuDidEndForElement:)] |
| 14893 | && ([delegate respondsToSelector:@selector(webView:shouldPreviewElement:)] |
| 14894 | || [delegate respondsToSelector:@selector(webView:previewingViewControllerForElement:defaultActions:)] |
| 14895 | || [delegate respondsToSelector:@selector(webView:commitPreviewingViewController:)] |
| 14896 | || [delegate respondsToSelector:@selector(_webView:previewViewControllerForURL:defaultActions:elementInfo:)] |
| 14897 | || [delegate respondsToSelector:@selector(_webView:previewViewControllerForURL:)] |
| 14898 | || [delegate respondsToSelector:@selector(_webView:previewViewControllerForImage:alternateURL:defaultActions:elementInfo:)]); |
| 14899 | } |
| 14900 | |
| 14901 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 14902 | static NSArray<WKPreviewAction *> *wkLegacyPreviewActionsFromElementActions(NSArray<_WKElementAction *> *elementActions, _WKActivatedElementInfo *elementInfo) |
| 14903 | { |
| 14904 | NSMutableArray<WKPreviewAction *> *previewActions = [NSMutableArray arrayWithCapacity:[elementActions count]]; |
| 14905 | for (_WKElementAction *elementAction in elementActions) { |
| 14906 | RetainPtr previewAction = [WKPreviewAction actionWithIdentifier:protect(previewIdentifierForElementAction(elementAction)).get() title:elementAction.title style:UIPreviewActionStyleDefault handler:^(UIPreviewAction *, UIViewController *) { |
| 14907 | [elementAction runActionWithElementInfo:elementInfo]; |
| 14908 | }]; |
| 14909 | previewAction.get().image = [_WKElementAction imageForElementActionType:elementAction.type]; |
| 14910 | [previewActions addObject:previewAction.get()]; |
| 14911 | } |
| 14912 | return previewActions; |
| 14913 | } |
| 14914 | |
| 14915 | static UIAction *uiActionForLegacyPreviewAction(UIPreviewAction *previewAction, UIViewController *previewViewController) |
| 14916 | { |
| 14917 | // UIPreviewActionItem.image is SPI, so no external clients will be able |
| 14918 | // to provide glyphs for actions <rdar://problem/50151855>. |
| 14919 | // However, they should migrate to the new API. |
| 14920 | |
| 14921 | return [UIAction actionWithTitle:previewAction.title image:previewAction.image identifier:nilnullptr handler:^(UIAction *action) { |
| 14922 | previewAction.handler(previewAction, previewViewController); |
| 14923 | }]; |
| 14924 | } |
| 14925 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 14926 | |
| 14927 | static NSArray<UIMenuElement *> *menuElementsFromLegacyPreview(UIViewController *previewViewController) |
| 14928 | { |
| 14929 | if (!previewViewController) |
| 14930 | return nilnullptr; |
| 14931 | |
| 14932 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 14933 | NSArray<id<UIPreviewActionItem>> *previewActions = previewViewController.previewActionItems; |
| 14934 | if (!previewActions || ![previewActions count]) |
| 14935 | return nilnullptr; |
| 14936 | |
| 14937 | auto actions = [NSMutableArray arrayWithCapacity:previewActions.count]; |
| 14938 | |
| 14939 | for (UIPreviewAction *previewAction in previewActions) |
| 14940 | [actions addObject:protect(uiActionForLegacyPreviewAction(previewAction, previewViewController)).get()]; |
| 14941 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 14942 | |
| 14943 | return actions; |
| 14944 | } |
| 14945 | |
| 14946 | static NSMutableArray<UIMenuElement *> *menuElementsFromDefaultActions(const RetainPtr<NSArray>& defaultElementActions, RetainPtr<_WKActivatedElementInfo> elementInfo) |
| 14947 | { |
| 14948 | if (!defaultElementActions || !defaultElementActions.get().count) |
| 14949 | return nilnullptr; |
| 14950 | |
| 14951 | auto actions = [NSMutableArray arrayWithCapacity:defaultElementActions.get().count]; |
| 14952 | |
| 14953 | for (_WKElementAction *elementAction in defaultElementActions.get()) |
| 14954 | [actions addObject:[elementAction uiActionForElementInfo:elementInfo.get()]]; |
| 14955 | |
| 14956 | return actions; |
| 14957 | } |
| 14958 | |
| 14959 | static UIMenu *menuFromLegacyPreviewOrDefaultActions(UIViewController *previewViewController, const RetainPtr<NSArray>& defaultElementActions, RetainPtr<_WKActivatedElementInfo> elementInfo, NSString *title = nilnullptr) |
| 14960 | { |
| 14961 | RetainPtr actions = menuElementsFromLegacyPreview(previewViewController); |
| 14962 | if (!actions) |
| 14963 | actions = menuElementsFromDefaultActions(defaultElementActions, elementInfo); |
| 14964 | |
| 14965 | return [UIMenu menuWithTitle:title children:actions.get()]; |
| 14966 | } |
| 14967 | |
| 14968 | - (void)assignLegacyDataForContextMenuInteraction |
| 14969 | { |
| 14970 | ASSERT(!_contextMenuHasRequestedLegacyData)((void)0); |
| 14971 | if (_contextMenuHasRequestedLegacyData) |
| 14972 | return; |
| 14973 | _contextMenuHasRequestedLegacyData = YES__objc_yes; |
| 14974 | |
| 14975 | if (!_webView) |
| 14976 | return; |
| 14977 | auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate); |
| 14978 | if (!uiDelegate) |
| 14979 | return; |
| 14980 | |
| 14981 | const auto& url = _positionInformation.url; |
| 14982 | |
| 14983 | protect(_page)->startInteractionWithPositionInformation(_positionInformation); |
| 14984 | |
| 14985 | RetainPtr<UIViewController> previewViewController; |
| 14986 | |
| 14987 | auto elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithInteractionInformationAtPosition:_positionInformation isUsingAlternateURLForImage:NO__objc_no userInfo:nilnullptr]); |
| 14988 | |
| 14989 | ASSERT_IMPLIES(_positionInformation.isImage, _positionInformation.image)((void)0); |
| 14990 | if (_positionInformation.isLink) { |
| 14991 | _longPressCanClick = NO__objc_no; |
| 14992 | |
| 14993 | RetainPtr<NSArray<_WKElementAction *>> defaultActionsFromAssistant = [protect(_actionSheetAssistant) defaultActionsForLinkSheet:elementInfo.get()]; |
| 14994 | |
| 14995 | // FIXME: Animated images go here. |
| 14996 | |
| 14997 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 14998 | if ([uiDelegate respondsToSelector:@selector(webView:previewingViewControllerForElement:defaultActions:)]) { |
| 14999 | RetainPtr defaultActions = wkLegacyPreviewActionsFromElementActions(defaultActionsFromAssistant.get(), elementInfo.get()); |
| 15000 | auto previewElementInfo = adoptNS([[WKPreviewElementInfo alloc] _initWithLinkURL:url.createNSURL().get()]); |
| 15001 | // FIXME: Clients using this legacy API will always show their previewViewController and ignore _showLinkPreviews. |
| 15002 | previewViewController = [uiDelegate webView:self.webView previewingViewControllerForElement:previewElementInfo.get() defaultActions:defaultActions.get()]; |
| 15003 | } else if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForURL:defaultActions:elementInfo:)]) |
| 15004 | previewViewController = [uiDelegate _webView:self.webView previewViewControllerForURL:url.createNSURL().get() defaultActions:defaultActionsFromAssistant.get() elementInfo:elementInfo.get()]; |
| 15005 | else if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForURL:)]) |
| 15006 | previewViewController = [uiDelegate _webView:self.webView previewViewControllerForURL:url.createNSURL().get()]; |
| 15007 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15008 | |
| 15009 | // Previously, UIPreviewItemController would detect the case where there was no previewViewController |
| 15010 | // and create one. We need to replicate this code for the new API. |
| 15011 | if (!previewViewController || [url.createNSURL() iTunesStoreURL]) { |
| 15012 | auto ddContextMenuActionClass = PAL::getDDContextMenuActionClassSingleton(); |
| 15013 | BEGIN_BLOCK_OBJC_EXCEPTIONS@try { |
| 15014 | NSDictionary *context = [self dataDetectionContextForPositionInformation:_positionInformation]; |
| 15015 | RetainPtr<UIContextMenuConfiguration> dataDetectorsResult = [ddContextMenuActionClass contextMenuConfigurationForURL:url.createNSURL().get() identifier:_positionInformation.dataDetectorIdentifier.createNSString().get() selectedText:self.selectedText results:_positionInformation.dataDetectorResults.get() inView:self context:context menuIdentifier:nilnullptr]; |
| 15016 | if (_showLinkPreviews && dataDetectorsResult && dataDetectorsResult.get().previewProvider) |
| 15017 | _contextMenuLegacyPreviewController = dataDetectorsResult.get().previewProvider(); |
| 15018 | if (dataDetectorsResult && dataDetectorsResult.get().actionProvider) { |
| 15019 | RetainPtr menuElements = menuElementsFromDefaultActions(defaultActionsFromAssistant, elementInfo); |
| 15020 | _contextMenuLegacyMenu = dataDetectorsResult.get().actionProvider(menuElements.get()); |
| 15021 | } |
| 15022 | END_BLOCK_OBJC_EXCEPTIONS} @catch(NSException *localException) { ReportBlockedObjCException (localException); } |
| 15023 | return; |
| 15024 | } |
| 15025 | |
| 15026 | _contextMenuLegacyMenu = menuFromLegacyPreviewOrDefaultActions(previewViewController.get(), defaultActionsFromAssistant, elementInfo); |
| 15027 | |
| 15028 | } else if (_positionInformation.isImage && _positionInformation.image) { |
| 15029 | RetainPtr nsURL = url.createNSURL(); |
| 15030 | RetainPtr<NSDictionary> imageInfo; |
| 15031 | RetainPtr cgImage = protect(_positionInformation.image)->createPlatformImage(); |
| 15032 | auto uiImage = adoptNS([[UIImage alloc] initWithCGImage:cgImage.get()]); |
| 15033 | |
| 15034 | if ([uiDelegate respondsToSelector:@selector(_webView:alternateURLFromImage:userInfo:)]) { |
| 15035 | NSDictionary *userInfo; |
| 15036 | nsURL = [uiDelegate _webView:self.webView alternateURLFromImage:uiImage.get() userInfo:&userInfo]; |
| 15037 | imageInfo = userInfo; |
| 15038 | } |
| 15039 | |
| 15040 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15041 | if ([uiDelegate respondsToSelector:@selector(_webView:willPreviewImageWithURL:)]) |
| 15042 | [uiDelegate _webView:self.webView willPreviewImageWithURL:_positionInformation.imageURL.createNSURL().get()]; |
| 15043 | |
| 15044 | RetainPtr<NSArray<_WKElementAction *>> defaultActionsFromAssistant = [protect(_actionSheetAssistant) defaultActionsForImageSheet:elementInfo.get()]; |
| 15045 | |
| 15046 | if (imageInfo && [uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForImage:alternateURL:defaultActions:elementInfo:)]) |
| 15047 | previewViewController = [uiDelegate _webView:self.webView previewViewControllerForImage:uiImage.get() alternateURL:nsURL.get() defaultActions:defaultActionsFromAssistant.get() elementInfo:elementInfo.get()]; |
| 15048 | else |
| 15049 | previewViewController = adoptNS([[WKImagePreviewViewController alloc] initWithCGImage:cgImage defaultActions:defaultActionsFromAssistant.get() elementInfo:elementInfo.get() presentingView:self]); |
| 15050 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15051 | |
| 15052 | _contextMenuLegacyMenu = menuFromLegacyPreviewOrDefaultActions(previewViewController.get(), defaultActionsFromAssistant, elementInfo, _positionInformation.title.createNSString().get()); |
| 15053 | } |
| 15054 | |
| 15055 | _contextMenuLegacyPreviewController = WTF::move(previewViewController); |
| 15056 | } |
| 15057 | |
| 15058 | - (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location |
| 15059 | { |
| 15060 | RetainPtr<UIContextMenuConfiguration> configuration; |
| 15061 | #if USE(BROWSERENGINEKIT)(defined 1 && 1) |
| 15062 | if (self._shouldUseUIContextMenuAsyncConfiguration) { |
| 15063 | auto asyncConfiguration = adoptNS([[BEContextMenuConfiguration alloc] init]); |
| 15064 | [self _internalContextMenuInteraction:interaction configurationForMenuAtLocation:location completion:[asyncConfiguration](UIContextMenuConfiguration *finalConfiguration) { |
| 15065 | [asyncConfiguration fulfillUsingConfiguration:finalConfiguration]; |
| 15066 | }]; |
| 15067 | configuration = asyncConfiguration.get(); |
| 15068 | } |
| 15069 | #endif // USE(BROWSERENGINEKIT) |
| 15070 | return configuration.autorelease(); |
| 15071 | } |
| 15072 | |
| 15073 | - (void)_internalContextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location completion:(void(^)(UIContextMenuConfiguration *))completion |
| 15074 | { |
| 15075 | _useContextMenuInteractionDismissalPreview = YES__objc_yes; |
| 15076 | |
| 15077 | if (!_webView) |
| 15078 | return completion(nilnullptr); |
| 15079 | |
| 15080 | if (!self.webView.configuration._longPressActionsEnabled) |
| 15081 | return completion(nilnullptr); |
| 15082 | |
| 15083 | auto getConfigurationAndContinue = [weakSelf = WeakObjCPtr<WKContentView>(self), interaction = retainPtr(interaction), completion = makeBlockPtr(completion)] (WebKit::ProceedWithTextSelectionInImage proceedWithTextSelectionInImage) { |
| 15084 | auto strongSelf = weakSelf.get(); |
| 15085 | if (!strongSelf || proceedWithTextSelectionInImage == WebKit::ProceedWithTextSelectionInImage::Yes) { |
| 15086 | completion(nilnullptr); |
| 15087 | return; |
| 15088 | } |
| 15089 | |
| 15090 | strongSelf->_showLinkPreviews = true; |
| 15091 | if (NSNumber *value = [[NSUserDefaults standardUserDefaults] objectForKey:webkitShowLinkPreviewsPreferenceKey]) |
| 15092 | strongSelf->_showLinkPreviews = value.boolValue; |
| 15093 | |
| 15094 | WebKit::InteractionInformationRequest request { WebCore::roundedIntPoint([interaction locationInView:strongSelf.get()]) }; |
| 15095 | request.includeSnapshot = true; |
| 15096 | request.includeLinkIndicator = true; |
| 15097 | request.linkIndicatorShouldHaveLegacyMargins = ![strongSelf _shouldUseContextMenus]; |
| 15098 | request.gatherAnimations = [strongSelf->_webView.get() _allowAnimationControls]; |
| 15099 | |
| 15100 | [strongSelf doAfterPositionInformationUpdate:[weakSelf = WeakObjCPtr<WKContentView>(strongSelf.get()), interaction, completion] (WebKit::InteractionInformationAtPosition) { |
| 15101 | if (auto strongSelf = weakSelf.get()) |
| 15102 | [strongSelf continueContextMenuInteraction:interaction completion:completion.get()]; |
| 15103 | else |
| 15104 | completion(nilnullptr); |
| 15105 | } forRequest:request]; |
| 15106 | }; |
| 15107 | |
| 15108 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 15109 | [self _doAfterPendingImageAnalysis:getConfigurationAndContinue]; |
| 15110 | #else |
| 15111 | getConfigurationAndContinue(WebKit::ProceedWithTextSelectionInImage::No); |
| 15112 | #endif |
| 15113 | } |
| 15114 | |
| 15115 | - (UIAction *)placeholderForDynamicallyInsertedImageAnalysisActions |
| 15116 | { |
| 15117 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 15118 | switch (_dynamicImageAnalysisContextMenuState) { |
| 15119 | case WebKit::DynamicImageAnalysisContextMenuState::NotWaiting: |
| 15120 | return nilnullptr; |
| 15121 | case WebKit::DynamicImageAnalysisContextMenuState::WaitingForImageAnalysis: |
| 15122 | case WebKit::DynamicImageAnalysisContextMenuState::WaitingForVisibleMenu: { |
| 15123 | // FIXME: This placeholder item warrants its own unique item identifier; however, to ensure that internal clients |
| 15124 | // that already allow image analysis items (e.g. Mail) continue to allow this placeholder item, we reuse an existing |
| 15125 | // image analysis identifier. |
| 15126 | RetainPtr placeholder = [UIAction actionWithTitle:@"" image:nilnullptr identifier:protect(elementActionTypeToUIActionIdentifier(_WKElementActionTypeRevealImage)).get() handler:^(UIAction *) { }]; |
| 15127 | [placeholder setAttributes:UIMenuElementAttributesHidden]; |
| 15128 | return placeholder.autorelease(); |
| 15129 | } |
| 15130 | } |
| 15131 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 15132 | return nilnullptr; |
| 15133 | } |
| 15134 | |
| 15135 | - (void)continueContextMenuInteraction:(UIContextMenuInteraction *)interaction completion:(void(^)(UIContextMenuConfiguration *))continueWithContextMenuConfiguration |
| 15136 | { |
| 15137 | if (!self.window) |
| 15138 | return continueWithContextMenuConfiguration(nilnullptr); |
| 15139 | |
| 15140 | if (!_positionInformation.touchCalloutEnabled) |
| 15141 | return continueWithContextMenuConfiguration(nilnullptr); |
| 15142 | |
| 15143 | if (!_positionInformation.isLink && !_positionInformation.isImage && !_positionInformation.isAttachment && !_positionInformation.isInPlugin && !self.positionInformationHasImageOverlayDataDetector) |
| 15144 | return continueWithContextMenuConfiguration(nilnullptr); |
| 15145 | |
| 15146 | URL linkURL = _positionInformation.url; |
| 15147 | |
| 15148 | if (_positionInformation.isLink && linkURL.isEmpty()) |
| 15149 | return continueWithContextMenuConfiguration(nilnullptr); |
| 15150 | |
| 15151 | auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate); |
| 15152 | |
| 15153 | if (needsDeprecatedPreviewAPI(uiDelegate)) { |
| 15154 | if (_positionInformation.isLink) { |
| 15155 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15156 | if ([uiDelegate respondsToSelector:@selector(webView:shouldPreviewElement:)]) { |
| 15157 | auto previewElementInfo = adoptNS([[WKPreviewElementInfo alloc] _initWithLinkURL:linkURL.createNSURL().get()]); |
| 15158 | if (![uiDelegate webView:self.webView shouldPreviewElement:previewElementInfo.get()]) |
| 15159 | return continueWithContextMenuConfiguration(nilnullptr); |
| 15160 | } |
| 15161 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15162 | |
| 15163 | // FIXME: Support JavaScript urls here. But make sure they don't show a preview. |
| 15164 | // <rdar://problem/50572283> |
| 15165 | if (!linkURL.protocolIsInHTTPFamily()) { |
| 15166 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15167 | if (!WebCore::DataDetection::canBePresentedByDataDetectors(linkURL)) |
| 15168 | return continueWithContextMenuConfiguration(nilnullptr); |
| 15169 | #endif |
| 15170 | } |
| 15171 | } |
| 15172 | |
| 15173 | _contextMenuLegacyPreviewController = nullptr; |
| 15174 | _contextMenuLegacyMenu = nullptr; |
| 15175 | _contextMenuHasRequestedLegacyData = NO__objc_no; |
| 15176 | _contextMenuActionProviderDelegateNeedsOverride = NO__objc_no; |
| 15177 | _contextMenuIsUsingAlternateURLForImage = NO__objc_no; |
| 15178 | |
| 15179 | UIContextMenuActionProvider actionMenuProvider = [weakSelf = WeakObjCPtr<WKContentView>(self)] (NSArray<UIMenuElement *> *) -> UIMenu * { |
| 15180 | auto strongSelf = weakSelf.get(); |
| 15181 | if (!strongSelf) |
| 15182 | return nilnullptr; |
| 15183 | if (!strongSelf->_contextMenuHasRequestedLegacyData) |
| 15184 | [strongSelf assignLegacyDataForContextMenuInteraction]; |
| 15185 | |
| 15186 | return strongSelf->_contextMenuLegacyMenu.get(); |
| 15187 | }; |
| 15188 | |
| 15189 | UIContextMenuContentPreviewProvider contentPreviewProvider = [weakSelf = WeakObjCPtr<WKContentView>(self)] () -> UIViewController * { |
| 15190 | auto strongSelf = weakSelf.get(); |
| 15191 | if (!strongSelf) |
| 15192 | return nilnullptr; |
| 15193 | if (!strongSelf->_contextMenuHasRequestedLegacyData) |
| 15194 | [strongSelf assignLegacyDataForContextMenuInteraction]; |
| 15195 | |
| 15196 | return strongSelf->_contextMenuLegacyPreviewController.get(); |
| 15197 | }; |
| 15198 | |
| 15199 | protect(_page)->startInteractionWithPositionInformation(_positionInformation); |
| 15200 | |
| 15201 | continueWithContextMenuConfiguration([UIContextMenuConfiguration configurationWithIdentifier:nilnullptr previewProvider:contentPreviewProvider actionProvider:actionMenuProvider]); |
| 15202 | return; |
| 15203 | } |
| 15204 | |
| 15205 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15206 | if ([linkURL.createNSURL() iTunesStoreURL]) { |
| 15207 | [self continueContextMenuInteractionWithDataDetectors:continueWithContextMenuConfiguration]; |
| 15208 | return; |
| 15209 | } |
| 15210 | #endif |
| 15211 | |
| 15212 | auto completionBlock = makeBlockPtr([interaction = retainPtr(interaction), continueWithContextMenuConfiguration = makeBlockPtr(continueWithContextMenuConfiguration), linkURL = WTF::move(linkURL), weakSelf = WeakObjCPtr<WKContentView>(self)] (UIContextMenuConfiguration *configurationFromWKUIDelegate) mutable { |
| 15213 | auto strongSelf = weakSelf.get(); |
| 15214 | if (!strongSelf) { |
| 15215 | continueWithContextMenuConfiguration(nilnullptr); |
| 15216 | return; |
| 15217 | } |
| 15218 | |
| 15219 | if (configurationFromWKUIDelegate) { |
| 15220 | protect(strongSelf->_page)->startInteractionWithPositionInformation(strongSelf->_positionInformation); |
| 15221 | strongSelf->_contextMenuActionProviderDelegateNeedsOverride = YES__objc_yes; |
| 15222 | continueWithContextMenuConfiguration(configurationFromWKUIDelegate); |
| 15223 | return; |
| 15224 | } |
| 15225 | |
| 15226 | bool canShowHTTPLinkOrDataDetectorPreview = ([&] { |
| 15227 | if (linkURL.protocolIsInHTTPFamily()) |
| 15228 | return true; |
| 15229 | |
| 15230 | if (WebCore::DataDetection::canBePresentedByDataDetectors(linkURL)) |
| 15231 | return true; |
| 15232 | |
| 15233 | if ([strongSelf positionInformationHasImageOverlayDataDetector]) |
| 15234 | return true; |
| 15235 | |
| 15236 | return false; |
| 15237 | })(); |
| 15238 | |
| 15239 | ASSERT_IMPLIES(strongSelf->_positionInformation.isImage, strongSelf->_positionInformation.image)((void)0); |
| 15240 | if (strongSelf->_positionInformation.isImage && strongSelf->_positionInformation.image && !canShowHTTPLinkOrDataDetectorPreview) { |
| 15241 | RetainPtr cgImage = protect(strongSelf->_positionInformation.image)->createPlatformImage(); |
| 15242 | |
| 15243 | strongSelf->_contextMenuActionProviderDelegateNeedsOverride = NO__objc_no; |
| 15244 | |
| 15245 | auto elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithInteractionInformationAtPosition:strongSelf->_positionInformation isUsingAlternateURLForImage:strongSelf->_contextMenuIsUsingAlternateURLForImage userInfo:nilnullptr]); |
| 15246 | |
| 15247 | UIContextMenuActionProvider actionMenuProvider = [weakSelf, elementInfo] (NSArray<UIMenuElement *> *) -> UIMenu * { |
| 15248 | auto strongSelf = weakSelf.get(); |
| 15249 | if (!strongSelf) |
| 15250 | return nilnullptr; |
| 15251 | |
| 15252 | RetainPtr<NSArray<_WKElementAction *>> defaultActionsFromAssistant = [strongSelf->_actionSheetAssistant defaultActionsForImageSheet:elementInfo.get()]; |
| 15253 | RetainPtr actions = menuElementsFromDefaultActions(defaultActionsFromAssistant, elementInfo); |
| 15254 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 15255 | if (auto *placeholder = [strongSelf placeholderForDynamicallyInsertedImageAnalysisActions]) |
| 15256 | [actions addObject:placeholder]; |
| 15257 | else if (UIMenu *menu = [strongSelf machineReadableCodeSubMenuForImageContextMenu]) |
| 15258 | [actions addObject:menu]; |
| 15259 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 15260 | return [UIMenu menuWithTitle:strongSelf->_positionInformation.title.createNSString().get() children:actions.get()]; |
| 15261 | }; |
| 15262 | |
| 15263 | UIContextMenuContentPreviewProvider contentPreviewProvider = [weakSelf, cgImage, elementInfo] () -> UIViewController * { |
| 15264 | auto strongSelf = weakSelf.get(); |
| 15265 | if (!strongSelf) |
| 15266 | return nilnullptr; |
| 15267 | |
| 15268 | auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>([strongSelf webViewUIDelegate]); |
| 15269 | if ([uiDelegate respondsToSelector:@selector(_webView:contextMenuContentPreviewForElement:)]) { |
| 15270 | if (UIViewController *previewViewController = [uiDelegate _webView:[strongSelf webView] contextMenuContentPreviewForElement:strongSelf->_contextMenuElementInfo.get()]) |
| 15271 | return previewViewController; |
| 15272 | } |
| 15273 | |
| 15274 | return adoptNS([[WKImagePreviewViewController alloc] initWithCGImage:cgImage defaultActions:nilnullptr elementInfo:elementInfo.get() presentingView:strongSelf.get()]).autorelease(); |
| 15275 | }; |
| 15276 | |
| 15277 | continueWithContextMenuConfiguration([UIContextMenuConfiguration configurationWithIdentifier:nilnullptr previewProvider:contentPreviewProvider actionProvider:actionMenuProvider]); |
| 15278 | return; |
| 15279 | } |
| 15280 | |
| 15281 | // At this point we have an object we might want to show a context menu for, but the |
| 15282 | // client was unable to handle it. Before giving up, we ask DataDetectors. |
| 15283 | |
| 15284 | strongSelf->_contextMenuElementInfo = nilnullptr; |
| 15285 | |
| 15286 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15287 | // FIXME: Support JavaScript urls here. But make sure they don't show a preview. |
| 15288 | // <rdar://problem/50572283> |
| 15289 | if (canShowHTTPLinkOrDataDetectorPreview) { |
| 15290 | [strongSelf continueContextMenuInteractionWithDataDetectors:continueWithContextMenuConfiguration.get()]; |
| 15291 | return; |
| 15292 | } |
| 15293 | #endif |
| 15294 | |
| 15295 | #if ENABLE(UNIFIED_PDF)(defined 1 && 1) |
| 15296 | if (strongSelf->_positionInformation.isInPlugin && [interaction menuAppearance] == UIContextMenuInteractionAppearanceCompact && [strongSelf shouldShowPDFDisplayOptions]) { |
| 15297 | [strongSelf continueContextMenuInteractionWithPDFDisplayOptions:continueWithContextMenuConfiguration.get()];; |
| 15298 | return; |
| 15299 | } |
| 15300 | #endif |
| 15301 | |
| 15302 | continueWithContextMenuConfiguration(nilnullptr); |
| 15303 | }); |
| 15304 | |
| 15305 | _contextMenuActionProviderDelegateNeedsOverride = NO__objc_no; |
| 15306 | _contextMenuIsUsingAlternateURLForImage = NO__objc_no; |
| 15307 | _contextMenuElementInfo = wrapper(API::ContextMenuElementInfo::create(_positionInformation, nilnullptr)); |
| 15308 | |
| 15309 | if (_positionInformation.isImage && _positionInformation.url.isNull() && [uiDelegate respondsToSelector:@selector(_webView:alternateURLFromImage:userInfo:)]) { |
| 15310 | UIImage *uiImage = [[_contextMenuElementInfo _activatedElementInfo] image]; |
| 15311 | NSDictionary *userInfo = nilnullptr; |
| 15312 | NSURL *nsURL = [uiDelegate _webView:self.webView alternateURLFromImage:uiImage userInfo:&userInfo]; |
| 15313 | _positionInformation.url = nsURL; |
| 15314 | if (nsURL) |
| 15315 | _contextMenuIsUsingAlternateURLForImage = YES__objc_yes; |
| 15316 | _contextMenuElementInfo = wrapper(API::ContextMenuElementInfo::create(_positionInformation, userInfo)); |
| 15317 | } |
| 15318 | |
| 15319 | if (_positionInformation.isLink && [uiDelegate respondsToSelector:@selector(webView:contextMenuConfigurationForElement:completionHandler:)]) { |
| 15320 | auto checker = WebKit::CompletionHandlerCallChecker::create(uiDelegate, @selector(webView:contextMenuConfigurationForElement:completionHandler:)); |
| 15321 | [uiDelegate webView:self.webView contextMenuConfigurationForElement:_contextMenuElementInfo.get() completionHandler:makeBlockPtr([completionBlock = WTF::move(completionBlock), checker = WTF::move(checker)] (UIContextMenuConfiguration *configuration) { |
| 15322 | if (checker->completionHandlerHasBeenCalled()) |
| 15323 | return; |
| 15324 | checker->didCallCompletionHandler(); |
| 15325 | completionBlock(configuration); |
| 15326 | }).get()]; |
| 15327 | } else if ([uiDelegate respondsToSelector:@selector(_webView:contextMenuConfigurationForElement:completionHandler:)]) { |
| 15328 | auto checker = WebKit::CompletionHandlerCallChecker::create(uiDelegate, @selector(_webView:contextMenuConfigurationForElement:completionHandler:)); |
| 15329 | [uiDelegate _webView:self.webView contextMenuConfigurationForElement:_contextMenuElementInfo.get() completionHandler:makeBlockPtr([completionBlock = WTF::move(completionBlock), checker = WTF::move(checker)] (UIContextMenuConfiguration *configuration) { |
| 15330 | if (checker->completionHandlerHasBeenCalled()) |
| 15331 | return; |
| 15332 | checker->didCallCompletionHandler(); |
| 15333 | completionBlock(configuration); |
| 15334 | }).get()]; |
| 15335 | } else |
| 15336 | completionBlock(nilnullptr); |
| 15337 | } |
| 15338 | |
| 15339 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15340 | |
| 15341 | - (void)continueContextMenuInteractionWithDataDetectors:(void(^)(UIContextMenuConfiguration *))continueWithContextMenuConfiguration |
| 15342 | { |
| 15343 | BEGIN_BLOCK_OBJC_EXCEPTIONS@try { |
| 15344 | auto ddContextMenuActionClass = PAL::getDDContextMenuActionClassSingleton(); |
| 15345 | auto context = retainPtr([self dataDetectionContextForPositionInformation:_positionInformation]); |
| 15346 | RetainPtr<UIContextMenuConfiguration> configurationFromDataDetectors; |
| 15347 | |
| 15348 | if (self.positionInformationHasImageOverlayDataDetector) { |
| 15349 | DDScannerResult *scannerResult = [_positionInformation.dataDetectorResults firstObject]; |
| 15350 | configurationFromDataDetectors = [ddContextMenuActionClass contextMenuConfigurationWithResult:RetainPtr { scannerResult.coreResult }.get() inView:self context:context.get() menuIdentifier:nilnullptr]; |
| 15351 | } else { |
| 15352 | configurationFromDataDetectors = [ddContextMenuActionClass contextMenuConfigurationForURL:_positionInformation.url.createNSURL().get() identifier:_positionInformation.dataDetectorIdentifier.createNSString().get() selectedText:[self selectedText] results:_positionInformation.dataDetectorResults.get() inView:self context:context.get() menuIdentifier:nilnullptr]; |
| 15353 | protect(_page)->startInteractionWithPositionInformation(_positionInformation); |
| 15354 | } |
| 15355 | |
| 15356 | _contextMenuActionProviderDelegateNeedsOverride = YES__objc_yes; |
| 15357 | continueWithContextMenuConfiguration(configurationFromDataDetectors.get()); |
| 15358 | END_BLOCK_OBJC_EXCEPTIONS} @catch(NSException *localException) { ReportBlockedObjCException (localException); } |
| 15359 | } |
| 15360 | |
| 15361 | #endif // ENABLE(DATA_DETECTION) |
| 15362 | |
| 15363 | #if ENABLE(UNIFIED_PDF)(defined 1 && 1) |
| 15364 | |
| 15365 | - (BOOL)shouldShowPDFDisplayOptions |
| 15366 | { |
| 15367 | return protect(_page->preferences())->twoUpPDFDisplayModeSupportEnabled() && self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular; |
| 15368 | } |
| 15369 | |
| 15370 | - (void)continueContextMenuInteractionWithPDFDisplayOptions:(void(^)(UIContextMenuConfiguration *))continueWithContextMenuConfiguration |
| 15371 | { |
| 15372 | static UIActionIdentifier const WKPDFActionSinglePageIdentifier = @"WKPDFActionSinglePageIdentifier"; |
| 15373 | static UIActionIdentifier const WKPDFActionTwoPagesIdentifier = @"WKPDFActionPlayTwoPagesIdentifier"; |
| 15374 | |
| 15375 | RetainPtr configuration = [UIContextMenuConfiguration configurationWithIdentifier:nilnullptr previewProvider:nilnullptr actionProvider:[weakSelf = WeakObjCPtr<WKContentView>(self)](NSArray<UIMenuElement *> *) -> UIMenu * { |
| 15376 | RetainPtr strongSelf = weakSelf.get(); |
| 15377 | if (!strongSelf) |
| 15378 | return nilnullptr; |
| 15379 | |
| 15380 | UIActionHandler actionHandler = [weakSelf](UIAction *action) { |
| 15381 | RetainPtr strongSelf = weakSelf.get(); |
| 15382 | if (!strongSelf) |
| 15383 | return; |
| 15384 | |
| 15385 | RefPtr page = strongSelf->_page; |
| 15386 | if (!page) |
| 15387 | return; |
| 15388 | |
| 15389 | BOOL isSinglePage = WebKit::isSinglePagePDFDisplayMode(page->pdfDisplayMode()); |
| 15390 | BOOL isSinglePageAction = [action.identifier isEqualToString:WKPDFActionSinglePageIdentifier]; |
| 15391 | |
| 15392 | if (isSinglePage == isSinglePageAction) |
| 15393 | return; |
| 15394 | |
| 15395 | page->requestPDFDisplayMode(isSinglePageAction ? WebKit::PDFDisplayMode::SinglePageContinuous : WebKit::PDFDisplayMode::TwoUpContinuous); |
| 15396 | }; |
| 15397 | |
| 15398 | RetainPtr singlePageAction = [UIAction actionWithTitle:WebCore::contextMenuItemPDFSinglePage().createNSString().get() image:nilnullptr identifier:WKPDFActionSinglePageIdentifier handler:actionHandler]; |
| 15399 | RetainPtr twoPagesAction = [UIAction actionWithTitle:WebCore::contextMenuItemPDFTwoPages().createNSString().get() image:nilnullptr identifier:WKPDFActionTwoPagesIdentifier handler:actionHandler]; |
| 15400 | |
| 15401 | if (WebKit::isSinglePagePDFDisplayMode(protect(strongSelf->_page)->pdfDisplayMode())) |
| 15402 | [singlePageAction setState:UIMenuElementStateOn]; |
| 15403 | else |
| 15404 | [twoPagesAction setState:UIMenuElementStateOn]; |
| 15405 | |
| 15406 | return [UIMenu menuWithTitle:@"" children:@[ singlePageAction.get(), twoPagesAction.get() ]]; |
| 15407 | }]; |
| 15408 | |
| 15409 | continueWithContextMenuConfiguration(configuration.get()); |
| 15410 | } |
| 15411 | |
| 15412 | #endif |
| 15413 | |
| 15414 | - (NSArray<UIMenuElement *> *)_contextMenuInteraction:(UIContextMenuInteraction *)interaction overrideSuggestedActionsForConfiguration:(UIContextMenuConfiguration *)configuration |
| 15415 | { |
| 15416 | // If we're here we're in the legacy path, which ignores the suggested actions anyway. |
| 15417 | if (!_contextMenuActionProviderDelegateNeedsOverride) |
| 15418 | return nilnullptr; |
| 15419 | |
| 15420 | RetainPtr suggestedActions = [protect(_actionSheetAssistant) suggestedActionsForContextMenuWithPositionInformation:_positionInformation]; |
| 15421 | if (auto *placeholder = self.placeholderForDynamicallyInsertedImageAnalysisActions) |
| 15422 | [suggestedActions addObject:placeholder]; |
| 15423 | return suggestedActions.autorelease(); |
| 15424 | } |
| 15425 | |
| 15426 | - (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configuration:(UIContextMenuConfiguration *)configuration highlightPreviewForItemWithIdentifier:(id<NSCopying>)identifier |
| 15427 | { |
| 15428 | [self _startSuppressingSelectionAssistantForReason:WebKit::InteractionIsHappening]; |
| 15429 | [self _cancelTouchEventGestureRecognizer]; |
| 15430 | _isPreparingToDisplayContextMenu = YES__objc_yes; |
| 15431 | return [self _createTargetedContextMenuHintPreviewIfPossible]; |
| 15432 | } |
| 15433 | |
| 15434 | - (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willDisplayMenuForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id<UIContextMenuInteractionAnimating>)animator |
| 15435 | { |
| 15436 | if (!_webView) |
| 15437 | return; |
| 15438 | |
| 15439 | protect(_page)->willBeginContextMenuInteraction(); |
| 15440 | _isPreparingToDisplayContextMenu = NO__objc_no; |
| 15441 | _isDisplayingContextMenuWithAnimation = YES__objc_yes; |
| 15442 | [animator addCompletion:[weakSelf = WeakObjCPtr<WKContentView>(self)] { |
| 15443 | if (auto strongSelf = weakSelf.get()) { |
| 15444 | ASSERT_IMPLIES(strongSelf->_isDisplayingContextMenuWithAnimation, [strongSelf->_contextMenuHintContainerView window])((void)0); |
| 15445 | strongSelf->_isDisplayingContextMenuWithAnimation = NO__objc_no; |
| 15446 | [strongSelf->_webView.get() _didShowContextMenu]; |
| 15447 | #if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1) |
| 15448 | switch (strongSelf->_dynamicImageAnalysisContextMenuState) { |
| 15449 | case WebKit::DynamicImageAnalysisContextMenuState::NotWaiting: |
| 15450 | case WebKit::DynamicImageAnalysisContextMenuState::WaitingForImageAnalysis: |
| 15451 | break; |
| 15452 | case WebKit::DynamicImageAnalysisContextMenuState::WaitingForVisibleMenu: |
| 15453 | [strongSelf _insertDynamicImageAnalysisContextMenuItemsIfPossible]; |
| 15454 | break; |
| 15455 | } |
| 15456 | #endif // ENABLE(IMAGE_ANALYSIS) |
| 15457 | } |
| 15458 | }]; |
| 15459 | |
| 15460 | auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate); |
| 15461 | if (!uiDelegate) |
| 15462 | return; |
| 15463 | |
| 15464 | if ([uiDelegate respondsToSelector:@selector(webView:contextMenuWillPresentForElement:)]) |
| 15465 | [uiDelegate webView:self.webView contextMenuWillPresentForElement:_contextMenuElementInfo.get()]; |
| 15466 | else if ([uiDelegate respondsToSelector:@selector(_webView:contextMenuWillPresentForElement:)]) { |
| 15467 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15468 | [uiDelegate _webView:self.webView contextMenuWillPresentForElement:_contextMenuElementInfo.get()]; |
| 15469 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15470 | } |
| 15471 | } |
| 15472 | |
| 15473 | - (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configuration:(UIContextMenuConfiguration *)configuration dismissalPreviewForItemWithIdentifier:(id<NSCopying>)identifier |
| 15474 | { |
| 15475 | if (!_useContextMenuInteractionDismissalPreview) { |
| 15476 | _contextMenuInteractionTargetedPreview = nilnullptr; |
| 15477 | return nilnullptr; |
| 15478 | } |
| 15479 | |
| 15480 | return std::exchange(_contextMenuInteractionTargetedPreview, nilnullptr).autorelease(); |
| 15481 | } |
| 15482 | |
| 15483 | - (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willPerformPreviewActionForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration animator:(id<UIContextMenuInteractionCommitAnimating>)animator |
| 15484 | { |
| 15485 | if (!_webView) |
| 15486 | return; |
| 15487 | |
| 15488 | _useContextMenuInteractionDismissalPreview = NO__objc_no; |
| 15489 | |
| 15490 | auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate); |
| 15491 | if (!uiDelegate) |
| 15492 | return; |
| 15493 | |
| 15494 | if (needsDeprecatedPreviewAPI(uiDelegate)) { |
| 15495 | |
| 15496 | if (_positionInformation.isImage) { |
| 15497 | if ([uiDelegate respondsToSelector:@selector(_webView:commitPreviewedImageWithURL:)]) { |
| 15498 | const auto& imageURL = _positionInformation.imageURL; |
| 15499 | if (imageURL.isEmpty() || !(imageURL.protocolIsInHTTPFamily() || imageURL.protocolIs("data"_s))) |
| 15500 | return; |
| 15501 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15502 | [uiDelegate _webView:self.webView commitPreviewedImageWithURL:imageURL.createNSURL().get()]; |
| 15503 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15504 | } |
| 15505 | return; |
| 15506 | } |
| 15507 | |
| 15508 | if ([uiDelegate respondsToSelector:@selector(webView:commitPreviewingViewController:)]) { |
| 15509 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15510 | if (RetainPtr viewController = _contextMenuLegacyPreviewController.get()) |
| 15511 | [uiDelegate webView:self.webView commitPreviewingViewController:viewController.get()]; |
| 15512 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15513 | return; |
| 15514 | } |
| 15515 | |
| 15516 | if ([uiDelegate respondsToSelector:@selector(_webView:commitPreviewedViewController:)]) { |
| 15517 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15518 | if (RetainPtr viewController = _contextMenuLegacyPreviewController.get()) |
| 15519 | [uiDelegate _webView:self.webView commitPreviewedViewController:viewController.get()]; |
| 15520 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15521 | return; |
| 15522 | } |
| 15523 | |
| 15524 | return; |
| 15525 | } |
| 15526 | |
| 15527 | if ([uiDelegate respondsToSelector:@selector(webView:contextMenuForElement:willCommitWithAnimator:)]) |
| 15528 | [uiDelegate webView:self.webView contextMenuForElement:_contextMenuElementInfo.get() willCommitWithAnimator:animator]; |
| 15529 | else if ([uiDelegate respondsToSelector:@selector(_webView:contextMenuForElement:willCommitWithAnimator:)]) { |
| 15530 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15531 | [uiDelegate _webView:self.webView contextMenuForElement:_contextMenuElementInfo.get() willCommitWithAnimator:animator]; |
| 15532 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15533 | } |
| 15534 | |
| 15535 | #if defined(DD_CONTEXT_MENU_SPI_VERSION) && DD_CONTEXT_MENU_SPI_VERSION >= 2 |
| 15536 | if ([configuration isKindOfClass:PAL::getDDContextMenuConfigurationClassSingleton()]) { |
| 15537 | DDContextMenuConfiguration *ddConfiguration = static_cast<DDContextMenuConfiguration *>(configuration); |
| 15538 | |
| 15539 | BOOL shouldExpandPreview = NO__objc_no; |
| 15540 | RetainPtr<UIViewController> presentedViewController; |
| 15541 | |
| 15542 | #if defined(DD_CONTEXT_MENU_SPI_VERSION) && DD_CONTEXT_MENU_SPI_VERSION >= 3 |
| 15543 | shouldExpandPreview = !!ddConfiguration.interactionViewControllerProvider; |
| 15544 | if (shouldExpandPreview) |
| 15545 | presentedViewController = ddConfiguration.interactionViewControllerProvider(); |
| 15546 | #else |
| 15547 | shouldExpandPreview = ddConfiguration.expandPreviewOnInteraction; |
| 15548 | presentedViewController = animator.previewViewController; |
| 15549 | #endif |
| 15550 | |
| 15551 | if (shouldExpandPreview) { |
| 15552 | animator.preferredCommitStyle = UIContextMenuInteractionCommitStylePop; |
| 15553 | _useContextMenuInteractionDismissalPreview = YES__objc_yes; |
| 15554 | |
| 15555 | // We will re-present modally on the same VC that is currently presenting the preview in a context menu. |
| 15556 | RetainPtr<UIViewController> presentingViewController = animator.previewViewController.presentingViewController; |
| 15557 | |
| 15558 | [animator addAnimations:^{ |
| 15559 | [presentingViewController presentViewController:presentedViewController.get() animated:NO__objc_no completion:nilnullptr]; |
| 15560 | }]; |
| 15561 | return; |
| 15562 | } |
| 15563 | |
| 15564 | if (NSURL *interactionURL = ddConfiguration.interactionURL) { |
| 15565 | animator.preferredCommitStyle = UIContextMenuInteractionCommitStylePop; |
| 15566 | _useContextMenuInteractionDismissalPreview = YES__objc_yes; |
| 15567 | |
| 15568 | [animator addAnimations:^{ |
| 15569 | [[UIApplication sharedApplication] openURL:interactionURL withCompletionHandler:nilnullptr]; |
| 15570 | }]; |
| 15571 | return; |
| 15572 | } |
| 15573 | } |
| 15574 | #endif |
| 15575 | } |
| 15576 | |
| 15577 | - (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willEndForConfiguration:(UIContextMenuConfiguration *)configuration animator:(id<UIContextMenuInteractionAnimating>)animator |
| 15578 | { |
| 15579 | if (!_webView) |
| 15580 | return; |
| 15581 | |
| 15582 | Ref page = *_page; |
| 15583 | page->didEndContextMenuInteraction(); |
| 15584 | // FIXME: This delegate is being called more than once by UIKit. <rdar://problem/51550291> |
| 15585 | // This conditional avoids the WKUIDelegate being called twice too. |
| 15586 | if (_contextMenuElementInfo) { |
| 15587 | auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate); |
| 15588 | if ([uiDelegate respondsToSelector:@selector(webView:contextMenuDidEndForElement:)]) |
| 15589 | [uiDelegate webView:self.webView contextMenuDidEndForElement:_contextMenuElementInfo.get()]; |
| 15590 | else if ([uiDelegate respondsToSelector:@selector(_webView:contextMenuDidEndForElement:)]) { |
| 15591 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15592 | [uiDelegate _webView:self.webView contextMenuDidEndForElement:_contextMenuElementInfo.get()]; |
| 15593 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15594 | } |
| 15595 | } |
| 15596 | |
| 15597 | page->stopInteraction(); |
| 15598 | |
| 15599 | _contextMenuLegacyPreviewController = nullptr; |
| 15600 | _contextMenuLegacyMenu = nullptr; |
| 15601 | _contextMenuHasRequestedLegacyData = NO__objc_no; |
| 15602 | _contextMenuElementInfo = nullptr; |
| 15603 | |
| 15604 | auto contextFinalizer = [weakSelf = WeakObjCPtr<WKContentView>(self)] () { |
| 15605 | auto strongSelf = weakSelf.get(); |
| 15606 | if (!strongSelf) |
| 15607 | return; |
| 15608 | |
| 15609 | strongSelf->_isDisplayingContextMenuWithAnimation = NO__objc_no; |
| 15610 | [strongSelf _removeContextMenuHintContainerIfPossible]; |
| 15611 | [strongSelf->_webView.get() _didDismissContextMenu]; |
| 15612 | |
| 15613 | [strongSelf _stopSuppressingSelectionAssistantForReason:WebKit::InteractionIsHappening]; |
| 15614 | }; |
| 15615 | |
| 15616 | if (animator) |
| 15617 | [animator addCompletion:contextFinalizer]; |
| 15618 | else |
| 15619 | contextFinalizer(); |
| 15620 | } |
| 15621 | |
| 15622 | #endif // USE(UICONTEXTMENU) |
| 15623 | |
| 15624 | - (BOOL)_interactionShouldBeginFromPreviewItemController:(UIPreviewItemController *)controller forPosition:(CGPoint)position |
| 15625 | { |
| 15626 | if (!_longPressCanClick) |
| 15627 | return NO__objc_no; |
| 15628 | |
| 15629 | WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(position)); |
| 15630 | request.includeSnapshot = true; |
| 15631 | request.includeLinkIndicator = true; |
| 15632 | request.linkIndicatorShouldHaveLegacyMargins = !self._shouldUseContextMenus; |
| 15633 | request.gatherAnimations = [self.webView _allowAnimationControls]; |
| 15634 | if (![self ensurePositionInformationIsUpToDate:request]) |
| 15635 | return NO__objc_no; |
| 15636 | if (!_positionInformation.isLink && !_positionInformation.isImage && !_positionInformation.isAttachment) |
| 15637 | return NO__objc_no; |
| 15638 | |
| 15639 | const URL& linkURL = _positionInformation.url; |
| 15640 | if (_positionInformation.isLink) { |
| 15641 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 15642 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15643 | if ([uiDelegate respondsToSelector:@selector(webView:shouldPreviewElement:)]) { |
| 15644 | auto previewElementInfo = adoptNS([[WKPreviewElementInfo alloc] _initWithLinkURL:linkURL.createNSURL().get()]); |
| 15645 | return [uiDelegate webView:self.webView shouldPreviewElement:previewElementInfo.get()]; |
| 15646 | } |
| 15647 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15648 | if (linkURL.isEmpty()) |
| 15649 | return NO__objc_no; |
| 15650 | if (linkURL.protocolIsInHTTPFamily()) |
| 15651 | return YES__objc_yes; |
| 15652 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15653 | if (WebCore::DataDetection::canBePresentedByDataDetectors(linkURL)) |
| 15654 | return YES__objc_yes; |
| 15655 | #endif |
| 15656 | return NO__objc_no; |
| 15657 | } |
| 15658 | return YES__objc_yes; |
| 15659 | } |
| 15660 | |
| 15661 | - (NSDictionary *)_dataForPreviewItemController:(UIPreviewItemController *)controller atPosition:(CGPoint)position type:(UIPreviewItemType *)type |
| 15662 | { |
| 15663 | *type = UIPreviewItemTypeNone; |
| 15664 | |
| 15665 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 15666 | BOOL supportsImagePreview = [uiDelegate respondsToSelector:@selector(_webView:commitPreviewedImageWithURL:)]; |
| 15667 | BOOL canShowImagePreview = _positionInformation.isImage && supportsImagePreview; |
| 15668 | BOOL canShowLinkPreview = _positionInformation.isLink || canShowImagePreview; |
| 15669 | BOOL useImageURLForLink = NO__objc_no; |
| 15670 | BOOL respondsToAttachmentListForWebViewSourceIsManaged = [uiDelegate respondsToSelector:@selector(_attachmentListForWebView:sourceIsManaged:)]; |
| 15671 | BOOL supportsAttachmentPreview = ([uiDelegate respondsToSelector:@selector(_attachmentListForWebView:)] || respondsToAttachmentListForWebViewSourceIsManaged) |
| 15672 | && [uiDelegate respondsToSelector:@selector(_webView:indexIntoAttachmentListForElement:)]; |
| 15673 | BOOL canShowAttachmentPreview = (_positionInformation.isAttachment || _positionInformation.isImage) && supportsAttachmentPreview; |
| 15674 | BOOL isDataDetectorLink = NO__objc_no; |
| 15675 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15676 | isDataDetectorLink = _positionInformation.isDataDetectorLink; |
| 15677 | #endif |
| 15678 | |
| 15679 | if (canShowImagePreview && _positionInformation.isAnimatedImage) { |
| 15680 | canShowImagePreview = NO__objc_no; |
| 15681 | canShowLinkPreview = YES__objc_yes; |
| 15682 | useImageURLForLink = YES__objc_yes; |
| 15683 | } |
| 15684 | |
| 15685 | if (!canShowLinkPreview && !canShowImagePreview && !canShowAttachmentPreview) |
| 15686 | return nilnullptr; |
| 15687 | |
| 15688 | const URL& linkURL = _positionInformation.url; |
| 15689 | if (!useImageURLForLink && (linkURL.isEmpty() || (!linkURL.protocolIsInHTTPFamily() && !isDataDetectorLink))) { |
| 15690 | if (canShowLinkPreview && !canShowImagePreview) |
| 15691 | return nilnullptr; |
| 15692 | canShowLinkPreview = NO__objc_no; |
| 15693 | } |
| 15694 | |
| 15695 | auto dataForPreview = adoptNS([[NSMutableDictionary alloc] init]); |
| 15696 | if (canShowLinkPreview) { |
| 15697 | *type = UIPreviewItemTypeLink; |
| 15698 | if (useImageURLForLink) |
| 15699 | dataForPreview.get()[UIPreviewDataLink] = _positionInformation.imageURL.createNSURL().get(); |
| 15700 | else |
| 15701 | dataForPreview.get()[UIPreviewDataLink] = linkURL.createNSURL().get(); |
| 15702 | #if ENABLE(DATA_DETECTION)(defined 1 && 1) |
| 15703 | if (isDataDetectorLink) { |
| 15704 | RetainPtr<NSDictionary> context; |
| 15705 | if ([uiDelegate respondsToSelector:@selector(_dataDetectionContextForWebView:)]) |
| 15706 | context = [uiDelegate _dataDetectionContextForWebView:self.webView]; |
| 15707 | |
| 15708 | DDDetectionController *controller = [PAL::getDDDetectionControllerClassSingleton() sharedController]; |
| 15709 | // We cannot use a RetainPtr since this is an out-parameter for [DDDetectionController resultForURL:]. |
| 15710 | SUPPRESS_UNRETAINED_LOCAL[[clang::suppress]] NSDictionary *rawNewContext = nilnullptr; |
| 15711 | RetainPtr<NSMutableDictionary> extendedContext; |
| 15712 | RetainPtr ddResult = [controller resultForURL:dataForPreview.get()[UIPreviewDataLink] identifier:_positionInformation.dataDetectorIdentifier.createNSString().get() selectedText:[self selectedText] results:_positionInformation.dataDetectorResults.get() context:context extendedContext:&rawNewContext]; |
| 15713 | RetainPtr newContext = rawNewContext; |
| 15714 | if (ddResult) |
| 15715 | dataForPreview.get()[UIPreviewDataDDResult] = (__bridge id)ddResult.get(); |
| 15716 | if (!_positionInformation.textBefore.isEmpty() || !_positionInformation.textAfter.isEmpty()) { |
| 15717 | extendedContext = adoptNS([@{ |
| 15718 | PAL::get_DataDetectorsUI_kDataDetectorsLeadingTextSingleton() : _positionInformation.textBefore.createNSString().get(), |
| 15719 | PAL::get_DataDetectorsUI_kDataDetectorsTrailingTextSingleton() : _positionInformation.textAfter.createNSString().get(), |
| 15720 | } mutableCopy]); |
| 15721 | |
| 15722 | if (newContext) |
| 15723 | [extendedContext addEntriesFromDictionary:newContext]; |
| 15724 | newContext = extendedContext.get(); |
| 15725 | } |
| 15726 | if (newContext) |
| 15727 | dataForPreview.get()[UIPreviewDataDDContext] = newContext.get(); |
| 15728 | } |
| 15729 | #endif // ENABLE(DATA_DETECTION) |
| 15730 | } else if (canShowImagePreview) { |
| 15731 | *type = UIPreviewItemTypeImage; |
| 15732 | dataForPreview.get()[UIPreviewDataLink] = _positionInformation.imageURL.createNSURL().get(); |
| 15733 | } else if (canShowAttachmentPreview) { |
| 15734 | *type = UIPreviewItemTypeAttachment; |
| 15735 | auto element = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeAttachment URL:linkURL.createNSURL().get() image:nilnullptr information:_positionInformation]); |
| 15736 | NSUInteger index = [uiDelegate _webView:self.webView indexIntoAttachmentListForElement:element.get()]; |
| 15737 | if (index != NSNotFound) { |
| 15738 | BOOL sourceIsManaged = NO__objc_no; |
| 15739 | if (respondsToAttachmentListForWebViewSourceIsManaged) |
| 15740 | dataForPreview.get()[UIPreviewDataAttachmentList] = [uiDelegate _attachmentListForWebView:self.webView sourceIsManaged:&sourceIsManaged]; |
| 15741 | else |
| 15742 | dataForPreview.get()[UIPreviewDataAttachmentList] = [uiDelegate _attachmentListForWebView:self.webView]; |
| 15743 | dataForPreview.get()[UIPreviewDataAttachmentIndex] = [NSNumber numberWithUnsignedInteger:index]; |
| 15744 | dataForPreview.get()[UIPreviewDataAttachmentListIsContentManaged] = [NSNumber numberWithBool:sourceIsManaged]; |
| 15745 | } |
| 15746 | } |
| 15747 | |
| 15748 | return dataForPreview.autorelease(); |
| 15749 | } |
| 15750 | |
| 15751 | - (CGRect)_presentationRectForPreviewItemController:(UIPreviewItemController *)controller |
| 15752 | { |
| 15753 | return _positionInformation.bounds; |
| 15754 | } |
| 15755 | |
| 15756 | - (UIViewController *)_presentedViewControllerForPreviewItemController:(UIPreviewItemController *)controller |
| 15757 | { |
| 15758 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 15759 | |
| 15760 | [_webView.get() _didShowContextMenu]; |
| 15761 | |
| 15762 | NSURL *targetURL = controller.previewData[UIPreviewDataLink]; |
| 15763 | URL coreTargetURL = targetURL; |
| 15764 | bool isValidURLForImagePreview = !coreTargetURL.isEmpty() && (coreTargetURL.protocolIsInHTTPFamily() || coreTargetURL.protocolIs("data"_s)); |
| 15765 | |
| 15766 | if ([_previewItemController type] == UIPreviewItemTypeLink) { |
| 15767 | _longPressCanClick = NO__objc_no; |
| 15768 | protect(_page)->startInteractionWithPositionInformation(_positionInformation); |
| 15769 | |
| 15770 | // Treat animated images like a link preview |
| 15771 | if (isValidURLForImagePreview && _positionInformation.isAnimatedImage) { |
| 15772 | auto animatedImageElementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:targetURL imageURL:nilnullptr information:_positionInformation]); |
| 15773 | |
| 15774 | if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForAnimatedImageAtURL:defaultActions:elementInfo:imageSize:)]) { |
| 15775 | RetainPtr<NSArray> actions = [protect(_actionSheetAssistant) defaultActionsForImageSheet:animatedImageElementInfo.get()]; |
| 15776 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15777 | return [uiDelegate _webView:self.webView previewViewControllerForAnimatedImageAtURL:targetURL defaultActions:actions.get() elementInfo:animatedImageElementInfo.get() imageSize:_positionInformation.image->size()]; |
| 15778 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15779 | } |
| 15780 | } |
| 15781 | |
| 15782 | auto elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeLink URL:targetURL imageURL:nilnullptr information:_positionInformation]); |
| 15783 | |
| 15784 | auto actions = [protect(_actionSheetAssistant) defaultActionsForLinkSheet:elementInfo.get()]; |
| 15785 | if ([uiDelegate respondsToSelector:@selector(webView:previewingViewControllerForElement:defaultActions:)]) { |
| 15786 | auto previewActions = adoptNS([[NSMutableArray alloc] init]); |
| 15787 | for (_WKElementAction *elementAction in actions.get()) { |
| 15788 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15789 | RetainPtr previewAction = [WKPreviewAction actionWithIdentifier:protect(previewIdentifierForElementAction(elementAction)).get() title:[elementAction title] style:UIPreviewActionStyleDefault handler:^(UIPreviewAction *, UIViewController *) { |
| 15790 | [elementAction runActionWithElementInfo:elementInfo.get()]; |
| 15791 | }]; |
| 15792 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15793 | [previewActions addObject:previewAction.get()]; |
| 15794 | } |
| 15795 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15796 | auto previewElementInfo = adoptNS([[WKPreviewElementInfo alloc] _initWithLinkURL:targetURL]); |
| 15797 | if (UIViewController *controller = [uiDelegate webView:self.webView previewingViewControllerForElement:previewElementInfo.get() defaultActions:previewActions.get()]) |
| 15798 | return controller; |
| 15799 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15800 | } |
| 15801 | |
| 15802 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15803 | if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForURL:defaultActions:elementInfo:)]) |
| 15804 | return [uiDelegate _webView:self.webView previewViewControllerForURL:targetURL defaultActions:actions.get() elementInfo:elementInfo.get()]; |
| 15805 | |
| 15806 | if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForURL:)]) |
| 15807 | return [uiDelegate _webView:self.webView previewViewControllerForURL:targetURL]; |
| 15808 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15809 | |
| 15810 | return nilnullptr; |
| 15811 | } |
| 15812 | |
| 15813 | if ([_previewItemController type] == UIPreviewItemTypeImage) { |
| 15814 | if (!isValidURLForImagePreview) |
| 15815 | return nilnullptr; |
| 15816 | |
| 15817 | RetainPtr<NSURL> alternateURL = targetURL; |
| 15818 | RetainPtr<NSDictionary> imageInfo; |
| 15819 | RetainPtr<CGImageRef> cgImage = protect(_positionInformation.image)->createPlatformImage(); |
| 15820 | RetainPtr<UIImage> uiImage = adoptNS([[UIImage alloc] initWithCGImage:cgImage.get()]); |
| 15821 | if ([uiDelegate respondsToSelector:@selector(_webView:alternateURLFromImage:userInfo:)]) { |
| 15822 | NSDictionary *userInfo; |
| 15823 | alternateURL = [uiDelegate _webView:self.webView alternateURLFromImage:uiImage.get() userInfo:&userInfo]; |
| 15824 | imageInfo = userInfo; |
| 15825 | } |
| 15826 | |
| 15827 | auto elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:alternateURL.get() imageURL:nilnullptr userInfo:imageInfo.get() information:_positionInformation]); |
| 15828 | protect(_page)->startInteractionWithPositionInformation(_positionInformation); |
| 15829 | |
| 15830 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15831 | if ([uiDelegate respondsToSelector:@selector(_webView:willPreviewImageWithURL:)]) |
| 15832 | [uiDelegate _webView:self.webView willPreviewImageWithURL:targetURL]; |
| 15833 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15834 | |
| 15835 | auto defaultActions = [protect(_actionSheetAssistant) defaultActionsForImageSheet:elementInfo.get()]; |
| 15836 | if (imageInfo && [uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForImage:alternateURL:defaultActions:elementInfo:)]) { |
| 15837 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15838 | UIViewController *previewViewController = [uiDelegate _webView:self.webView previewViewControllerForImage:uiImage.get() alternateURL:alternateURL.get() defaultActions:defaultActions.get() elementInfo:elementInfo.get()]; |
| 15839 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15840 | if (previewViewController) |
| 15841 | return previewViewController; |
| 15842 | } |
| 15843 | |
| 15844 | return adoptNS([[WKImagePreviewViewController alloc] initWithCGImage:cgImage defaultActions:defaultActions elementInfo:elementInfo presentingView:self]).autorelease(); |
| 15845 | } |
| 15846 | |
| 15847 | return nilnullptr; |
| 15848 | } |
| 15849 | |
| 15850 | - (void)_previewItemController:(UIPreviewItemController *)controller commitPreview:(UIViewController *)viewController |
| 15851 | { |
| 15852 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 15853 | if ([_previewItemController type] == UIPreviewItemTypeImage) { |
| 15854 | if ([uiDelegate respondsToSelector:@selector(_webView:commitPreviewedImageWithURL:)]) { |
| 15855 | const URL& imageURL = _positionInformation.imageURL; |
| 15856 | if (imageURL.isEmpty() || !(imageURL.protocolIsInHTTPFamily() || imageURL.protocolIs("data"_s))) |
| 15857 | return; |
| 15858 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15859 | [uiDelegate _webView:self.webView commitPreviewedImageWithURL:imageURL.createNSURL().get()]; |
| 15860 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15861 | return; |
| 15862 | } |
| 15863 | return; |
| 15864 | } |
| 15865 | |
| 15866 | if ([uiDelegate respondsToSelector:@selector(webView:commitPreviewingViewController:)]) { |
| 15867 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15868 | [uiDelegate webView:self.webView commitPreviewingViewController:viewController]; |
| 15869 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15870 | return; |
| 15871 | } |
| 15872 | |
| 15873 | if ([uiDelegate respondsToSelector:@selector(_webView:commitPreviewedViewController:)]) { |
| 15874 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15875 | [uiDelegate _webView:self.webView commitPreviewedViewController:viewController]; |
| 15876 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15877 | return; |
| 15878 | } |
| 15879 | |
| 15880 | } |
| 15881 | |
| 15882 | - (void)_interactionStartedFromPreviewItemController:(UIPreviewItemController *)controller |
| 15883 | { |
| 15884 | [self _removeDefaultGestureRecognizers]; |
| 15885 | |
| 15886 | [self _cancelInteraction]; |
| 15887 | } |
| 15888 | |
| 15889 | - (void)_interactionStoppedFromPreviewItemController:(UIPreviewItemController *)controller |
| 15890 | { |
| 15891 | [self _addDefaultGestureRecognizers]; |
| 15892 | |
| 15893 | if (![protect(_actionSheetAssistant) isShowingSheet]) |
| 15894 | protect(_page)->stopInteraction(); |
| 15895 | } |
| 15896 | |
| 15897 | - (void)_previewItemController:(UIPreviewItemController *)controller didDismissPreview:(UIViewController *)viewController committing:(BOOL)committing |
| 15898 | { |
| 15899 | RetainPtr uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView.get() UIDelegate]); |
| 15900 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 15901 | if ([uiDelegate respondsToSelector:@selector(_webView:didDismissPreviewViewController:committing:)]) |
| 15902 | [uiDelegate _webView:self.webView didDismissPreviewViewController:viewController committing:committing]; |
| 15903 | else if ([uiDelegate respondsToSelector:@selector(_webView:didDismissPreviewViewController:)]) |
| 15904 | [uiDelegate _webView:self.webView didDismissPreviewViewController:viewController]; |
| 15905 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 15906 | |
| 15907 | [_webView.get() _didDismissContextMenu]; |
| 15908 | } |
| 15909 | |
| 15910 | - (UIImage *)_presentationSnapshotForPreviewItemController:(UIPreviewItemController *)controller |
| 15911 | { |
| 15912 | if (!_positionInformation.textIndicator->contentImage()) |
| 15913 | return nullptr; |
| 15914 | |
| 15915 | auto nativeImage = protect(_positionInformation.textIndicator->contentImage())->nativeImage(); |
| 15916 | if (!nativeImage) |
| 15917 | return nullptr; |
| 15918 | |
| 15919 | return adoptNS([[UIImage alloc] initWithCGImage:nativeImage->platformImage().get()]).autorelease(); |
| 15920 | } |
| 15921 | |
| 15922 | - (NSArray *)_presentationRectsForPreviewItemController:(UIPreviewItemController *)controller |
| 15923 | { |
| 15924 | if (_positionInformation.textIndicator->contentImage()) { |
| 15925 | auto origin = _positionInformation.textIndicator->textBoundingRectInRootViewCoordinates().location(); |
| 15926 | return createNSArray(_positionInformation.textIndicator->textRectsInBoundingRectCoordinates(), [&] (CGRect rect) { |
| 15927 | return [NSValue valueWithCGRect:CGRectOffset(rect, origin.x(), origin.y())]; |
| 15928 | }).autorelease(); |
| 15929 | } else { |
| 15930 | float marginInPx = 4 * protect(_page)->deviceScaleFactor(); |
| 15931 | return @[[NSValue valueWithCGRect:CGRectInset(_positionInformation.bounds, -marginInPx, -marginInPx)]]; |
| 15932 | } |
| 15933 | } |
| 15934 | |
| 15935 | - (void)_previewItemControllerDidCancelPreview:(UIPreviewItemController *)controller |
| 15936 | { |
| 15937 | _longPressCanClick = NO__objc_no; |
| 15938 | |
| 15939 | [_webView.get() _didDismissContextMenu]; |
| 15940 | } |
| 15941 | |
| 15942 | @end |
| 15943 | |
| 15944 | #endif // HAVE(LINK_PREVIEW) |
| 15945 | |
| 15946 | // UITextRange and UITextPosition implementations for WK2 |
| 15947 | // FIXME: Move these out into separate files. |
| 15948 | |
| 15949 | @implementation WKTextRange (UITextInputAdditions) |
| 15950 | |
| 15951 | - (BOOL)_isCaret |
| 15952 | { |
| 15953 | return self.empty; |
| 15954 | } |
| 15955 | |
| 15956 | - (BOOL)_isRanged |
| 15957 | { |
| 15958 | return !self.empty; |
| 15959 | } |
| 15960 | |
| 15961 | @end |
| 15962 | |
| 15963 | @implementation WKTextRange |
| 15964 | |
| 15965 | + (WKTextRange *)textRangeWithState:(BOOL)isNone isRange:(BOOL)isRange isEditable:(BOOL)isEditable startRect:(CGRect)startRect endRect:(CGRect)endRect selectionRects:(NSArray *)selectionRects selectedTextLength:(NSUInteger)selectedTextLength |
| 15966 | { |
| 15967 | auto range = adoptNS([[WKTextRange alloc] init]); |
| 15968 | [range setIsNone:isNone]; |
| 15969 | [range setIsRange:isRange]; |
| 15970 | [range setIsEditable:isEditable]; |
| 15971 | [range setStartRect:startRect]; |
| 15972 | [range setEndRect:endRect]; |
| 15973 | [range setSelectedTextLength:selectedTextLength]; |
| 15974 | [range setSelectionRects:selectionRects]; |
| 15975 | return range.autorelease(); |
| 15976 | } |
| 15977 | |
| 15978 | - (void)dealloc |
| 15979 | { |
| 15980 | [super dealloc]; |
| 15981 | } |
| 15982 | |
| 15983 | - (NSArray<WKTextSelectionRect *> *)selectionRects |
| 15984 | { |
| 15985 | return _selectionRects.get(); |
| 15986 | } |
| 15987 | |
| 15988 | - (void)setSelectionRects:(NSArray<WKTextSelectionRect *> *)selectionRects |
| 15989 | { |
| 15990 | _selectionRects = adoptNS([selectionRects copy]); |
| 15991 | } |
| 15992 | |
| 15993 | - (NSString *)description |
| 15994 | { |
| 15995 | return [NSString stringWithFormat:@"%@(%p) - start:%@, end:%@", [self class], self, protect(NSStringFromCGRect(self.startRect)).get(), protect(NSStringFromCGRect(self.endRect)).get()]; |
| 15996 | } |
| 15997 | |
| 15998 | - (WKTextPosition *)start |
| 15999 | { |
| 16000 | WKTextPosition *position = [WKTextPosition textPositionWithRect:self.startRect]; |
| 16001 | OptionSet anchors { WebKit::TextPositionAnchor::Start }; |
| 16002 | if (self.isEmpty) |
| 16003 | anchors.add(WebKit::TextPositionAnchor::End); |
| 16004 | position.anchors = anchors; |
| 16005 | return position; |
| 16006 | } |
| 16007 | |
| 16008 | - (WKTextPosition *)end |
| 16009 | { |
| 16010 | WKTextPosition *position = [WKTextPosition textPositionWithRect:self.endRect]; |
| 16011 | OptionSet anchors { WebKit::TextPositionAnchor::End }; |
| 16012 | if (self.isEmpty) |
| 16013 | anchors.add(WebKit::TextPositionAnchor::Start); |
| 16014 | position.anchors = anchors; |
| 16015 | return position; |
| 16016 | } |
| 16017 | |
| 16018 | - (BOOL)isEmpty |
| 16019 | { |
| 16020 | return !self.isRange; |
| 16021 | } |
| 16022 | |
| 16023 | // FIXME: Overriding isEqual: without overriding hash will cause trouble if this ever goes into an NSSet or is the key in an NSDictionary, |
| 16024 | // since two equal items could have different hashes. |
| 16025 | - (BOOL)isEqual:(id)other |
| 16026 | { |
| 16027 | if (![other isKindOfClass:[WKTextRange class]]) |
| 16028 | return NO__objc_no; |
| 16029 | |
| 16030 | WKTextRange *otherRange = (WKTextRange *)other; |
| 16031 | |
| 16032 | if (self == other) |
| 16033 | return YES__objc_yes; |
| 16034 | |
| 16035 | // FIXME: Probably incorrect for equality to ignore so much of the object state. |
| 16036 | // It ignores isNone, isEditable, selectedTextLength, and selectionRects. |
| 16037 | |
| 16038 | if (self.isRange) { |
| 16039 | if (!otherRange.isRange) |
| 16040 | return NO__objc_no; |
| 16041 | return CGRectEqualToRect(self.startRect, otherRange.startRect) && CGRectEqualToRect(self.endRect, otherRange.endRect); |
| 16042 | } else { |
| 16043 | if (otherRange.isRange) |
| 16044 | return NO__objc_no; |
| 16045 | // FIXME: Do we need to check isNone here? |
| 16046 | return CGRectEqualToRect(self.startRect, otherRange.startRect); |
| 16047 | } |
| 16048 | } |
| 16049 | |
| 16050 | @end |
| 16051 | |
| 16052 | @implementation WKTextPosition |
| 16053 | |
| 16054 | @synthesize positionRect = _positionRect; |
| 16055 | |
| 16056 | + (WKTextPosition *)textPositionWithRect:(CGRect)positionRect |
| 16057 | { |
| 16058 | auto pos = adoptNS([[WKTextPosition alloc] init]); |
| 16059 | [pos setPositionRect:positionRect]; |
| 16060 | return pos.autorelease(); |
| 16061 | } |
| 16062 | |
| 16063 | // FIXME: Overriding isEqual: without overriding hash will cause trouble if this ever goes into a NSSet or is the key in an NSDictionary, |
| 16064 | // since two equal items could have different hashes. |
| 16065 | - (BOOL)isEqual:(id)other |
| 16066 | { |
| 16067 | if (![other isKindOfClass:[WKTextPosition class]]) |
| 16068 | return NO__objc_no; |
| 16069 | |
| 16070 | return CGRectEqualToRect(self.positionRect, ((WKTextPosition *)other).positionRect); |
| 16071 | } |
| 16072 | |
| 16073 | - (NSString *)description |
| 16074 | { |
| 16075 | return [NSString stringWithFormat:@"<WKTextPosition: %p, {%@}>", self, protect(NSStringFromCGRect(self.positionRect)).get()]; |
| 16076 | } |
| 16077 | |
| 16078 | @end |
| 16079 | |
| 16080 | #if HAVE(UIFINDINTERACTION)(defined 1 && 1) |
| 16081 | |
| 16082 | @implementation WKFoundTextRange |
| 16083 | |
| 16084 | + (WKFoundTextRange *)foundTextRangeWithWebFoundTextRange:(WebKit::WebFoundTextRange)webRange |
| 16085 | { |
| 16086 | RetainPtr range = WTF::switchOn(webRange.data, |
| 16087 | [] (const WebKit::WebFoundTextRange::DOMData& domData) -> RetainPtr<WKFoundTextRange> { |
| 16088 | RetainPtr foundDOMTextRange = adoptNS([[WKFoundDOMTextRange alloc] init]); |
| 16089 | [foundDOMTextRange setLocation:domData.location]; |
| 16090 | [foundDOMTextRange setLength:domData.length]; |
| 16091 | return foundDOMTextRange; |
| 16092 | }, |
| 16093 | [] (const WebKit::WebFoundTextRange::PDFData& pdfData) -> RetainPtr<WKFoundTextRange> { |
| 16094 | RetainPtr foundPDFTextRange = adoptNS([[WKFoundPDFTextRange alloc] init]); |
| 16095 | [foundPDFTextRange setStartPage:pdfData.startPage]; |
| 16096 | [foundPDFTextRange setStartPageOffset:pdfData.startOffset]; |
| 16097 | [foundPDFTextRange setEndPage:pdfData.endPage]; |
| 16098 | [foundPDFTextRange setEndPageOffset:pdfData.endOffset]; |
| 16099 | return foundPDFTextRange; |
| 16100 | } |
| 16101 | ); |
| 16102 | |
| 16103 | [range setPathToFrame:createNSArray(webRange.pathToFrame, [](uint64_t index) { |
| 16104 | return @(index); |
| 16105 | }).get()]; |
| 16106 | [range setOrder:webRange.order]; |
| 16107 | return range.autorelease(); |
| 16108 | } |
| 16109 | |
| 16110 | - (void)dealloc |
| 16111 | { |
| 16112 | [super dealloc]; |
| 16113 | } |
| 16114 | |
| 16115 | - (BOOL)isEmpty |
| 16116 | { |
| 16117 | return NO__objc_no; |
| 16118 | } |
| 16119 | |
| 16120 | - (WebKit::WebFoundTextRange)webFoundTextRange |
| 16121 | { |
| 16122 | return { }; |
| 16123 | } |
| 16124 | |
| 16125 | @end |
| 16126 | |
| 16127 | @implementation WKFoundTextPosition |
| 16128 | @end |
| 16129 | |
| 16130 | @implementation WKFoundDOMTextRange |
| 16131 | |
| 16132 | - (WKFoundDOMTextPosition *)start |
| 16133 | { |
| 16134 | WKFoundDOMTextPosition *position = [WKFoundDOMTextPosition textPositionWithOffset:self.location order:self.order]; |
| 16135 | return position; |
| 16136 | } |
| 16137 | |
| 16138 | - (WKFoundDOMTextPosition *)end |
| 16139 | { |
| 16140 | WKFoundDOMTextPosition *position = [WKFoundDOMTextPosition textPositionWithOffset:(self.location + self.length) order:self.order]; |
| 16141 | return position; |
| 16142 | } |
| 16143 | |
| 16144 | - (WebKit::WebFoundTextRange)webFoundTextRange |
| 16145 | { |
| 16146 | WebKit::WebFoundTextRange::DOMData data { self.location, self.length }; |
| 16147 | auto pathToFrameVector = makeVector(self.pathToFrame, [](id number) -> std::optional<uint64_t> { |
| 16148 | return [number unsignedLongValue]; |
| 16149 | }); |
| 16150 | return { data, WTF::move(pathToFrameVector), self.order }; |
| 16151 | } |
| 16152 | |
| 16153 | @end |
| 16154 | |
| 16155 | @implementation WKFoundDOMTextPosition |
| 16156 | |
| 16157 | + (WKFoundDOMTextPosition *)textPositionWithOffset:(NSUInteger)offset order:(NSUInteger)order |
| 16158 | { |
| 16159 | RetainPtr pos = adoptNS([[WKFoundDOMTextPosition alloc] init]); |
| 16160 | [pos setOffset:offset]; |
| 16161 | [pos setOrder:order]; |
| 16162 | return pos.autorelease(); |
| 16163 | } |
| 16164 | |
| 16165 | @end |
| 16166 | |
| 16167 | @implementation WKFoundPDFTextRange |
| 16168 | |
| 16169 | - (WKFoundPDFTextPosition *)start |
| 16170 | { |
| 16171 | WKFoundPDFTextPosition *position = [WKFoundPDFTextPosition textPositionWithPage:self.startPage offset:self.startPageOffset]; |
| 16172 | return position; |
| 16173 | } |
| 16174 | |
| 16175 | - (WKFoundPDFTextPosition *)end |
| 16176 | { |
| 16177 | WKFoundPDFTextPosition *position = [WKFoundPDFTextPosition textPositionWithPage:self.endPage offset:self.endPageOffset]; |
| 16178 | return position; |
| 16179 | } |
| 16180 | |
| 16181 | - (WebKit::WebFoundTextRange)webFoundTextRange |
| 16182 | { |
| 16183 | WebKit::WebFoundTextRange::PDFData data { self.startPage, self.startPageOffset, self.endPage, self.endPageOffset }; |
| 16184 | auto pathToFrameVector = makeVector(self.pathToFrame, [](id number) -> std::optional<uint64_t> { |
| 16185 | return [number unsignedLongValue]; |
| 16186 | }); |
| 16187 | return { data, WTF::move(pathToFrameVector), self.order }; |
| 16188 | } |
| 16189 | |
| 16190 | @end |
| 16191 | |
| 16192 | @implementation WKFoundPDFTextPosition |
| 16193 | |
| 16194 | + (WKFoundPDFTextPosition *)textPositionWithPage:(NSUInteger)page offset:(NSUInteger)offset |
| 16195 | { |
| 16196 | RetainPtr pos = adoptNS([[WKFoundPDFTextPosition alloc] init]); |
| 16197 | [pos setPage:page]; |
| 16198 | [pos setOffset:offset]; |
| 16199 | return pos.autorelease(); |
| 16200 | } |
| 16201 | |
| 16202 | @end |
| 16203 | |
| 16204 | #endif |
| 16205 | |
| 16206 | @implementation WKAutocorrectionRects |
| 16207 | |
| 16208 | + (WKAutocorrectionRects *)autocorrectionRectsWithFirstCGRect:(CGRect)firstRect lastCGRect:(CGRect)lastRect |
| 16209 | { |
| 16210 | auto rects = adoptNS([[WKAutocorrectionRects alloc] init]); |
| 16211 | [rects setFirstRect:firstRect]; |
| 16212 | [rects setLastRect:lastRect]; |
| 16213 | return rects.autorelease(); |
| 16214 | } |
| 16215 | |
| 16216 | @end |
| 16217 | |
| 16218 | @implementation WKAutocorrectionContext |
| 16219 | |
| 16220 | + (WKAutocorrectionContext *)emptyAutocorrectionContext |
| 16221 | { |
| 16222 | return [self autocorrectionContextWithWebContext:WebKit::WebAutocorrectionContext { }]; |
| 16223 | } |
| 16224 | |
| 16225 | + (WKAutocorrectionContext *)autocorrectionContextWithWebContext:(const WebKit::WebAutocorrectionContext&)webCorrection |
| 16226 | { |
| 16227 | auto correction = adoptNS([[WKAutocorrectionContext alloc] init]); |
| 16228 | [correction setContextBeforeSelection:nsStringNilIfEmpty(webCorrection.contextBefore).get()]; |
| 16229 | [correction setSelectedText:nsStringNilIfEmpty(webCorrection.selectedText).get()]; |
| 16230 | [correction setMarkedText:nsStringNilIfEmpty(webCorrection.markedText).get()]; |
| 16231 | [correction setContextAfterSelection:nsStringNilIfEmpty(webCorrection.contextAfter).get()]; |
| 16232 | [correction setRangeInMarkedText:webCorrection.selectedRangeInMarkedText]; |
| 16233 | return correction.autorelease(); |
| 16234 | } |
| 16235 | |
| 16236 | @end |
| 16237 | |
| 16238 | #endif // PLATFORM(IOS_FAMILY) |