Title: [290450] trunk
Revision
290450
Author
wenson_hs...@apple.com
Date
2022-02-24 12:13:49 -0800 (Thu, 24 Feb 2022)

Log Message

Refactor logic for showing "Markup Image" and Quick Note items in the callout bar
https://bugs.webkit.org/show_bug.cgi?id=237127
rdar://89396617

Reviewed by Megan Gardner.

Source/WebKit:

Use `-buildMenuWithBuilder:` to supply additional Quick Note and image analysis items, instead of adding them
directly to the shared menu controller. See below for more details.

Tests:  ImageAnalysisTests.MenuControllerItems
        WebKit.AppHighlightsInImageOverlays

* UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView didMoveToWindow]):

See below.

(-[WKWebView buildMenuWithBuilder:]):

Override this method and call into the content view via `-buildMenuForWebViewWithBuilder:` to populate the given
builder with either Quick Note or "Markup Image" items, if necessary. Note that overriding this method on
WKWebView is sufficient, since UIKit walks up the responder hierarchy starting from the first responder
(WKContentView) when populating menu items.

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

Move some logic for conditionally enabling the Quick Note items into `-imageAnalysisMarkupMenu`.

(-[WKContentView imageAnalysisMarkupMenu]):
(-[WKContentView _selectionChanged]):

Remove logic that updates the shared UIMenuController's menu items every time the selection changes. This is
no longer needed, since UIKit will always call into `-buildMenuWithBuilder:` when presenting the callout bar,
which allows us to lazily run logic to determine whether or not we should show the Quick Note and image analysis
items when the callout bar is shown, instead of keeping the menu controller's `-menuItems` array up to date upon
each selection change.

(-[WKContentView buildMenuForWebViewWithBuilder:]):

Consult `-appHighlightMenu` and `-imageAnalysisMarkupMenu` to populate the menu with inline UIMenus. Note that
these menus' items are always inlined into the callout bar as top-level items, since we explicitly specify
`UIMenuOptionsDisplayInline` when creating the menu.

(-[WKContentView menuWithInlineAction:identifier:handler:]):
(-[WKContentView appHighlightMenu]):

Refactor this, and `-imageAnalysisMarkupMenu` above to return a UIMenu with a single UIAction presented inline.

(-[WKContentView updateImageAnalysisMarkupMenuItems:]): Deleted.

Replaced with `-imageAnalysisMarkupMenu`.

(-[WKContentView canPerformImageAnalysisMarkup]): Deleted.
(-[WKContentView performImageAnalysisMarkup:]): Deleted.
(-[WKContentView setUpAdditionalMenuControllerActions]): Deleted.
(findMenuItemWithAction): Deleted.
(-[WKContentView updateAppHighlightMenuItems:]): Deleted.

Replaced with `-appHighlightMenu`.

(-[WKContentView createHighlightForCurrentQuickNoteWithRange:]): Deleted.
(-[WKContentView createHighlightForNewQuickNoteWithRange:]): Deleted.

Tools:

Adjust a couple of existing API tests. See comments below.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/ImageAnalysisTests.mm:

Refactor ImageAnalysisTests.MenuControllerItems to exercise `-buildMenuWithBuilder:` instead of swizzling
UIMenuController methods and checking `-menuItems`.

(TestWebKitAPI::swizzledSetMenuItems): Deleted.
(): Deleted.
* TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm:

Refactor WebKit.AppHighlightsInImageOverlays to exercise `-buildMenuWithBuilder:` instead of passing in actions
to `-targetForAction:withSender:`.

* TestWebKitAPI/ios/TestUIMenuBuilder.h: Added.
* TestWebKitAPI/ios/TestUIMenuBuilder.mm: Added.

Add a helper class that conforms to UIMenuBuilder. This is used in the two API tests above as an argument to
`-buildMenuWithBuilder:` in order to collect additional menu items that WKWebView adds to the callout bar.

(-[TestUIMenuBuilder init]):
(-[TestUIMenuBuilder system]):
(-[TestUIMenuBuilder menuForIdentifier:]):
(-[TestUIMenuBuilder findMatching:]):
(-[TestUIMenuBuilder containsActionWithTitle:]):
(-[TestUIMenuBuilder reset]):
(-[TestUIMenuBuilder actionForIdentifier:]):
(-[TestUIMenuBuilder commandForAction:propertyList:]):
(-[TestUIMenuBuilder replaceMenuForIdentifier:withMenu:]):
(-[TestUIMenuBuilder replaceChildrenOfMenuForIdentifier:fromChildrenBlock:]):
(-[TestUIMenuBuilder insertChildMenu:atStartOfMenuForIdentifier:]):
(-[TestUIMenuBuilder insertChildMenu:atEndOfMenuForIdentifier:]):
(-[TestUIMenuBuilder removeMenuForIdentifier:]):
(-[TestUIMenuBuilder insertSiblingMenu:beforeMenuForIdentifier:]):
(-[TestUIMenuBuilder insertSiblingMenu:afterMenuForIdentifier:]):
(-[TestUIMenuBuilder registerMenu:]):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (290449 => 290450)


--- trunk/Source/WebKit/ChangeLog	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Source/WebKit/ChangeLog	2022-02-24 20:13:49 UTC (rev 290450)
@@ -1,3 +1,70 @@
+2022-02-24  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        Refactor logic for showing "Markup Image" and Quick Note items in the callout bar
+        https://bugs.webkit.org/show_bug.cgi?id=237127
+        rdar://89396617
+
+        Reviewed by Megan Gardner.
+
+        Use `-buildMenuWithBuilder:` to supply additional Quick Note and image analysis items, instead of adding them
+        directly to the shared menu controller. See below for more details.
+
+        Tests:  ImageAnalysisTests.MenuControllerItems
+                WebKit.AppHighlightsInImageOverlays
+
+        * UIProcess/API/ios/WKWebViewIOS.mm:
+        (-[WKWebView didMoveToWindow]):
+
+        See below.
+
+        (-[WKWebView buildMenuWithBuilder:]):
+
+        Override this method and call into the content view via `-buildMenuForWebViewWithBuilder:` to populate the given
+        builder with either Quick Note or "Markup Image" items, if necessary. Note that overriding this method on
+        WKWebView is sufficient, since UIKit walks up the responder hierarchy starting from the first responder
+        (WKContentView) when populating menu items.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView targetForAction:withSender:]):
+
+        Move some logic for conditionally enabling the Quick Note items into `-imageAnalysisMarkupMenu`.
+
+        (-[WKContentView imageAnalysisMarkupMenu]):
+        (-[WKContentView _selectionChanged]):
+
+        Remove logic that updates the shared UIMenuController's menu items every time the selection changes. This is
+        no longer needed, since UIKit will always call into `-buildMenuWithBuilder:` when presenting the callout bar,
+        which allows us to lazily run logic to determine whether or not we should show the Quick Note and image analysis
+        items when the callout bar is shown, instead of keeping the menu controller's `-menuItems` array up to date upon
+        each selection change.
+
+        (-[WKContentView buildMenuForWebViewWithBuilder:]):
+
+        Consult `-appHighlightMenu` and `-imageAnalysisMarkupMenu` to populate the menu with inline UIMenus. Note that
+        these menus' items are always inlined into the callout bar as top-level items, since we explicitly specify
+        `UIMenuOptionsDisplayInline` when creating the menu.
+
+        (-[WKContentView menuWithInlineAction:identifier:handler:]):
+        (-[WKContentView appHighlightMenu]):
+
+        Refactor this, and `-imageAnalysisMarkupMenu` above to return a UIMenu with a single UIAction presented inline.
+
+        (-[WKContentView updateImageAnalysisMarkupMenuItems:]): Deleted.
+
+        Replaced with `-imageAnalysisMarkupMenu`.
+
+        (-[WKContentView canPerformImageAnalysisMarkup]): Deleted.
+        (-[WKContentView performImageAnalysisMarkup:]): Deleted.
+        (-[WKContentView setUpAdditionalMenuControllerActions]): Deleted.
+        (findMenuItemWithAction): Deleted.
+        (-[WKContentView updateAppHighlightMenuItems:]): Deleted.
+
+        Replaced with `-appHighlightMenu`.
+
+        (-[WKContentView createHighlightForCurrentQuickNoteWithRange:]): Deleted.
+        (-[WKContentView createHighlightForNewQuickNoteWithRange:]): Deleted.
+
 2022-02-24  Chris Dumez  <cdu...@apple.com>
 
         Regression(r273929) FrameState no longer gets a move constructor

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm (290449 => 290450)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2022-02-24 20:13:49 UTC (rev 290450)
@@ -1538,8 +1538,6 @@
         [self _dispatchSetDeviceOrientation:[self _deviceOrientation]];
     _page->activityStateDidChange(WebCore::ActivityState::allFlags());
     _page->webViewDidMoveToWindow();
-
-    [_contentView setUpAdditionalMenuControllerActions];
 }
 
 - (void)_setOpaqueInternal:(BOOL)opaque
@@ -2702,6 +2700,14 @@
         _page->setUserInterfaceLayoutDirection(toUserInterfaceLayoutDirection(contentAttribute));
 }
 
+- (void)buildMenuWithBuilder:(id <UIMenuBuilder>)builder
+{
+    if (self.usesStandardContentView)
+        [_contentView buildMenuForWebViewWithBuilder:builder];
+
+    [super buildMenuWithBuilder:builder];
+}
+
 @end
 
 @implementation WKWebView (WKPrivateIOS)

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (290449 => 290450)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2022-02-24 20:13:49 UTC (rev 290450)
@@ -262,6 +262,7 @@
 }
 
 @class WKFocusedElementInfo;
+@protocol UIMenuBuilder;
 @protocol WKFormControl;
 
 @interface WKFormInputSession : NSObject <_WKFormInputSession>
@@ -593,6 +594,8 @@
 
 - (void)scrollViewWillStartPanOrPinchGesture;
 
+- (void)buildMenuForWebViewWithBuilder:(id <UIMenuBuilder>)builder;
+
 - (BOOL)canBecomeFirstResponderForWebView;
 - (BOOL)becomeFirstResponderForWebView;
 - (BOOL)resignFirstResponderForWebView;
@@ -782,8 +785,6 @@
 - (WebCore::DataOwnerType)_dataOwnerForPasteboard:(WebKit::PasteboardAccessIntent)intent;
 #endif
 
-- (void)setUpAdditionalMenuControllerActions;
-
 #if ENABLE(IMAGE_ANALYSIS)
 - (void)_endImageAnalysisGestureDeferral:(WebKit::ShouldPreventGestures)shouldPreventGestures;
 - (void)requestTextRecognition:(NSURL *)imageURL imageData:(const WebKit::ShareableBitmap::Handle&)imageData identifier:(NSString *)identifier completionHandler:(CompletionHandler<void(WebCore::TextRecognitionResult&&)>&&)completion;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (290449 => 290450)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2022-02-24 20:13:49 UTC (rev 290450)
@@ -4106,16 +4106,6 @@
 
 - (id)targetForAction:(SEL)action withSender:(id)sender
 {
-#if ENABLE(APP_HIGHLIGHTS)
-    if (action == @selector(createHighlightForCurrentQuickNoteWithRange:))
-        return self.shouldAllowAppHighlightCreation && _page->appHighlightsVisibility() ? self : nil;
-    if (action == @selector(createHighlightForNewQuickNoteWithRange:))
-        return self.shouldAllowAppHighlightCreation && !_page->appHighlightsVisibility() ? self : nil;
-#endif
-#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
-    if (action == @selector(performImageAnalysisMarkup:))
-        return self.canPerformImageAnalysisMarkup ? self : nil;
-#endif
     return [_webView targetForAction:action withSender:sender];
 }
 
@@ -4693,39 +4683,22 @@
 
 #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
 
-- (void)updateImageAnalysisMarkupMenuItems:(NSMutableArray<UIMenuItem *> *)updatedItems
+- (UIMenu *)imageAnalysisMarkupMenu
 {
-    auto currentItem = findMenuItemWithAction(updatedItems, @selector(performImageAnalysisMarkup:));
-    auto& editorState = _page->editorState();
-    if (!_page->preferences().imageAnalysisMarkupEnabled() || !self.window || editorState.isMissingPostLayoutData || !editorState.postLayoutData().selectedEditableImage) {
-        if (currentItem)
-            [updatedItems removeObject:currentItem];
-    } else if (!currentItem) {
-        auto item = adoptNS([[UIMenuItem alloc] initWithTitle:WebCore::contextMenuItemTitleMarkupImage() action:@selector(performImageAnalysisMarkup:)]);
-        [updatedItems insertObject:item.get() atIndex:0];
-    }
-}
-
-- (BOOL)canPerformImageAnalysisMarkup
-{
     if (!_page || !_page->preferences().imageAnalysisMarkupEnabled())
-        return NO;
+        return nil;
 
-    if (!_imageAnalysisMarkupData)
-        return NO;
+    if (_page->editorState().isMissingPostLayoutData || !_page->editorState().postLayoutData().selectedEditableImage)
+        return nil;
 
-    auto [elementContext, image, preferredMIMEType] = *_imageAnalysisMarkupData;
-    return !_page->editorState().isMissingPostLayoutData && elementContext == _page->editorState().postLayoutData().selectedEditableImage;
-}
+    return [self menuWithInlineAction:WebCore::contextMenuItemTitleMarkupImage() identifier:@"WKActionMarkupImage" handler:[](WKContentView *view) {
+        if (!view->_imageAnalysisMarkupData)
+            return;
 
-- (void)performImageAnalysisMarkup:(id)sender
-{
-    if (!self.canPerformImageAnalysisMarkup)
-        return;
-
-    auto [elementContext, image, preferredMIMEType] = *_imageAnalysisMarkupData;
-    if (auto [data, type] = WebKit::transcodeWithPreferredMIMEType(image.get(), preferredMIMEType.createCFString().get(), (__bridge CFStringRef)UTTypeTIFF.identifier); data)
-        _page->replaceWithPasteboardData(elementContext, { String { type.get() } }, { static_cast<const uint8_t*>([data bytes]), [data length] });
+        auto [elementContext, image, preferredMIMEType] = *view->_imageAnalysisMarkupData;
+        if (auto [data, type] = WebKit::transcodeWithPreferredMIMEType(image.get(), preferredMIMEType.createCFString().get(), (__bridge CFStringRef)UTTypeTIFF.identifier); data)
+            view->_page->replaceWithPasteboardData(elementContext, { String { type.get() } }, { static_cast<const uint8_t*>([data bytes]), [data length] });
+    }];
 }
 
 - (void)doAfterComputingImageAnalysisResultsForMarkup:(CompletionHandler<void()>&&)completion
@@ -7472,7 +7445,6 @@
 {
     _autocorrectionContextNeedsUpdate = YES;
 
-    [self setUpAdditionalMenuControllerActions];
     [self _updateSelectionAssistantSuppressionState];
 
     _cachedSelectedTextRange = nil;
@@ -9774,63 +9746,42 @@
 }
 #endif
 
-- (void)setUpAdditionalMenuControllerActions
+- (void)buildMenuForWebViewWithBuilder:(id <UIMenuBuilder>)builder
 {
-    auto updatedItems = adoptNS(UIMenuController.sharedMenuController.menuItems.mutableCopy ?: [NSMutableArray<UIMenuItem *> new]);
 #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
-    [self updateImageAnalysisMarkupMenuItems:updatedItems.get()];
+    if (auto menu = self.imageAnalysisMarkupMenu)
+        [builder insertSiblingMenu:menu afterMenuForIdentifier:UIMenuStandardEdit];
 #endif
+
 #if ENABLE(APP_HIGHLIGHTS)
-    [self updateAppHighlightMenuItems:updatedItems.get()];
+    if (auto menu = self.appHighlightMenu)
+        [builder insertChildMenu:menu atEndOfMenuForIdentifier:UIMenuRoot];
 #endif
-    UIMenuController.sharedMenuController.menuItems = updatedItems.get();
 }
 
-#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) || ENABLE(APP_HIGHLIGHTS)
-
-static UIMenuItem *findMenuItemWithAction(NSArray<UIMenuItem *> *items, SEL action)
+- (UIMenu *)menuWithInlineAction:(NSString *)title identifier:(NSString *)identifier handler:(Function<void(WKContentView *)>&&)handler
 {
-    for (UIMenuItem *item in items) {
-        if (item.action == action)
-            return item;
-    }
-    return nil;
+    auto action = "" actionWithTitle:title image:nil identifier:identifier handler:makeBlockPtr([handler = WTFMove(handler), weakSelf = WeakObjCPtr<WKContentView>(self)](UIAction *) mutable {
+        if (auto strongSelf = weakSelf.get())
+            handler(strongSelf.get());
+    }).get()];
+    return [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:@[ action ]];
 }
 
-#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) || ENABLE(APP_HIGHLIGHTS)
-
 #if ENABLE(APP_HIGHLIGHTS)
 
-- (void)updateAppHighlightMenuItems:(NSMutableArray<UIMenuItem *> *)updatedItems
+- (UIMenu *)appHighlightMenu
 {
-    auto currentQuickNoteItem = findMenuItemWithAction(updatedItems, @selector(createHighlightForCurrentQuickNoteWithRange:));
-    auto newQuickNoteItem = findMenuItemWithAction(updatedItems, @selector(createHighlightForNewQuickNoteWithRange:));
+    if (!_page->preferences().appHighlightsEnabled() || !_page->editorState().selectionIsRange || !self.shouldAllowAppHighlightCreation)
+        return nil;
 
-    if (!_page->preferences().appHighlightsEnabled() || !self.window || !_page->editorState().selectionIsRange) {
-        if (currentQuickNoteItem)
-            [updatedItems removeObject:currentQuickNoteItem];
-        if (newQuickNoteItem)
-            [updatedItems removeObject:newQuickNoteItem];
-        return;
-    }
-
-    if (!currentQuickNoteItem)
-        [updatedItems addObject:adoptNS([[UIMenuItem alloc] initWithTitle:WebCore::contextMenuItemTagAddHighlightToCurrentQuickNote() action:@selector(createHighlightForCurrentQuickNoteWithRange:)]).get()];
-
-    if (!newQuickNoteItem)
-        [updatedItems addObject:adoptNS([[UIMenuItem alloc] initWithTitle:WebCore::contextMenuItemTagAddHighlightToNewQuickNote() action:@selector(createHighlightForNewQuickNoteWithRange:)]).get()];
+    bool isVisible = _page->appHighlightsVisibility();
+    auto title = isVisible ? WebCore::contextMenuItemTagAddHighlightToCurrentQuickNote() : WebCore::contextMenuItemTagAddHighlightToNewQuickNote();
+    return [self menuWithInlineAction:title identifier:@"WKActionCreateQuickNote" handler:[isVisible](WKContentView *view) mutable {
+        view->_page->createAppHighlightInSelectedRange(isVisible ? WebCore::CreateNewGroupForHighlight::No : WebCore::CreateNewGroupForHighlight::Yes, WebCore::HighlightRequestOriginatedInApp::No);
+    }];
 }
 
-- (void)createHighlightForCurrentQuickNoteWithRange:(id)sender
-{
-    _page->createAppHighlightInSelectedRange(WebCore::CreateNewGroupForHighlight::No, WebCore::HighlightRequestOriginatedInApp::No);
-}
-
-- (void)createHighlightForNewQuickNoteWithRange:(id)sender
-{
-    _page->createAppHighlightInSelectedRange(WebCore::CreateNewGroupForHighlight::Yes, WebCore::HighlightRequestOriginatedInApp::No);
-}
-
 #endif // ENABLE(APP_HIGHLIGHTS)
 
 - (void)setContinuousSpellCheckingEnabled:(BOOL)enabled

Modified: trunk/Tools/ChangeLog (290449 => 290450)


--- trunk/Tools/ChangeLog	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Tools/ChangeLog	2022-02-24 20:13:49 UTC (rev 290450)
@@ -1,3 +1,49 @@
+2022-02-24  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        Refactor logic for showing "Markup Image" and Quick Note items in the callout bar
+        https://bugs.webkit.org/show_bug.cgi?id=237127
+        rdar://89396617
+
+        Reviewed by Megan Gardner.
+
+        Adjust a couple of existing API tests. See comments below.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/ImageAnalysisTests.mm:
+
+        Refactor ImageAnalysisTests.MenuControllerItems to exercise `-buildMenuWithBuilder:` instead of swizzling
+        UIMenuController methods and checking `-menuItems`.
+
+        (TestWebKitAPI::swizzledSetMenuItems): Deleted.
+        (): Deleted.
+        * TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm:
+
+        Refactor WebKit.AppHighlightsInImageOverlays to exercise `-buildMenuWithBuilder:` instead of passing in actions
+        to `-targetForAction:withSender:`.
+
+        * TestWebKitAPI/ios/TestUIMenuBuilder.h: Added.
+        * TestWebKitAPI/ios/TestUIMenuBuilder.mm: Added.
+
+        Add a helper class that conforms to UIMenuBuilder. This is used in the two API tests above as an argument to
+        `-buildMenuWithBuilder:` in order to collect additional menu items that WKWebView adds to the callout bar.
+
+        (-[TestUIMenuBuilder init]):
+        (-[TestUIMenuBuilder system]):
+        (-[TestUIMenuBuilder menuForIdentifier:]):
+        (-[TestUIMenuBuilder findMatching:]):
+        (-[TestUIMenuBuilder containsActionWithTitle:]):
+        (-[TestUIMenuBuilder reset]):
+        (-[TestUIMenuBuilder actionForIdentifier:]):
+        (-[TestUIMenuBuilder commandForAction:propertyList:]):
+        (-[TestUIMenuBuilder replaceMenuForIdentifier:withMenu:]):
+        (-[TestUIMenuBuilder replaceChildrenOfMenuForIdentifier:fromChildrenBlock:]):
+        (-[TestUIMenuBuilder insertChildMenu:atStartOfMenuForIdentifier:]):
+        (-[TestUIMenuBuilder insertChildMenu:atEndOfMenuForIdentifier:]):
+        (-[TestUIMenuBuilder removeMenuForIdentifier:]):
+        (-[TestUIMenuBuilder insertSiblingMenu:beforeMenuForIdentifier:]):
+        (-[TestUIMenuBuilder insertSiblingMenu:afterMenuForIdentifier:]):
+        (-[TestUIMenuBuilder registerMenu:]):
+
 2022-02-24  Jonathan Bedard  <jbed...@apple.com>
 
         [Python3] Convert shebangs in generate_xcfilelists_lib

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (290449 => 290450)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2022-02-24 20:13:49 UTC (rev 290450)
@@ -1112,6 +1112,7 @@
 		F4D060082734A1AB008FA67A /* simple-editor.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4D060072734A08C008FA67A /* simple-editor.html */; };
 		F4D4F3B61E4E2BCB00BB2767 /* DragAndDropSimulatorIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D4F3B41E4E2BCB00BB2767 /* DragAndDropSimulatorIOS.mm */; };
 		F4D4F3B91E4E36E400BB2767 /* DragAndDropTestsIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D4F3B71E4E36E400BB2767 /* DragAndDropTestsIOS.mm */; };
+		F4D5C55827C6FF4800ED1C23 /* TestUIMenuBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D5C55727C6EBDC00ED1C23 /* TestUIMenuBuilder.mm */; };
 		F4D5D69525AF8BE400205280 /* DisableAutomaticSpellingCorrection.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D5D69425AF8BE400205280 /* DisableAutomaticSpellingCorrection.mm */; };
 		F4D5E4E81F0C5D38008C1A49 /* dragstart-clear-selection.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */; };
 		F4D65DA81F5E4704009D8C27 /* selected-text-image-link-and-editable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4D65DA71F5E46C0009D8C27 /* selected-text-image-link-and-editable.html */; };
@@ -3129,6 +3130,8 @@
 		F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RunScriptAfterDocumentLoad.mm; sourceTree = "<group>"; };
 		F4D4F3B41E4E2BCB00BB2767 /* DragAndDropSimulatorIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DragAndDropSimulatorIOS.mm; sourceTree = "<group>"; };
 		F4D4F3B71E4E36E400BB2767 /* DragAndDropTestsIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DragAndDropTestsIOS.mm; sourceTree = "<group>"; };
+		F4D5C55627C6EBDC00ED1C23 /* TestUIMenuBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestUIMenuBuilder.h; sourceTree = "<group>"; };
+		F4D5C55727C6EBDC00ED1C23 /* TestUIMenuBuilder.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestUIMenuBuilder.mm; sourceTree = "<group>"; };
 		F4D5D69425AF8BE400205280 /* DisableAutomaticSpellingCorrection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DisableAutomaticSpellingCorrection.mm; sourceTree = "<group>"; };
 		F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "dragstart-clear-selection.html"; sourceTree = "<group>"; };
 		F4D65DA71F5E46C0009D8C27 /* selected-text-image-link-and-editable.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "selected-text-image-link-and-editable.html"; sourceTree = "<group>"; };
@@ -3734,6 +3737,8 @@
 				F4D4F3B41E4E2BCB00BB2767 /* DragAndDropSimulatorIOS.mm */,
 				2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */,
 				F48D6C0F224B377000E3E2FB /* PreferredContentMode.mm */,
+				F4D5C55627C6EBDC00ED1C23 /* TestUIMenuBuilder.h */,
+				F4D5C55727C6EBDC00ED1C23 /* TestUIMenuBuilder.mm */,
 				F4517B652054C49500C26721 /* TestWKWebViewController.h */,
 				F4517B662054C49500C26721 /* TestWKWebViewController.mm */,
 				2D2171C3262F8B8200C209DC /* UIKitMacHelperSPI.h */,
@@ -5799,6 +5804,7 @@
 				F45D3891215A7B4B002A2979 /* TestInspectorBar.mm in Sources */,
 				516281252325C18000BB7E42 /* TestPDFDocument.mm in Sources */,
 				7B774906267CCE72009873B4 /* TestRunnerTests.cpp in Sources */,
+				F4D5C55827C6FF4800ED1C23 /* TestUIMenuBuilder.mm in Sources */,
 				F4517B672054C49500C26721 /* TestWKWebViewController.mm in Sources */,
 				F45033F5206BEC95009351CE /* TextAutosizingBoost.mm in Sources */,
 				93E6193B1F931B3A00AF245E /* TextCodec.cpp in Sources */,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ImageAnalysisTests.mm (290449 => 290450)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ImageAnalysisTests.mm	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ImageAnalysisTests.mm	2022-02-24 20:13:49 UTC (rev 290450)
@@ -31,6 +31,7 @@
 #import "InstanceMethodSwizzler.h"
 #import "PlatformUtilities.h"
 #import "TestInputDelegate.h"
+#import "TestUIMenuBuilder.h"
 #import "TestWKWebView.h"
 #import "WKWebViewConfigurationExtras.h"
 #import <WebCore/LocalizedStrings.h>
@@ -263,27 +264,8 @@
 
 #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) && PLATFORM(IOS_FAMILY)
 
-static NeverDestroyed<RetainPtr<NSArray<UIMenuItem *>>> gSwizzledMenuItems;
-static void swizzledSetMenuItems(id, SEL, NSArray<UIMenuItem *> *items)
-{
-    gSwizzledMenuItems.get() = items;
-}
-
-static NSArray<UIMenuItem *> *swizzledMenuItems(id, SEL)
-{
-    return gSwizzledMenuItems.get().get();
-}
-
 TEST(ImageAnalysisTests, MenuControllerItems)
 {
-    auto sharedMenuController = UIMenuController.sharedMenuController;
-    InstanceMethodSwizzler menuItemsSwizzler { sharedMenuController.class, @selector(menuItems), reinterpret_cast<IMP>(swizzledMenuItems) };
-    InstanceMethodSwizzler setMenuItemsSwizzler { sharedMenuController.class, @selector(setMenuItems:), reinterpret_cast<IMP>(swizzledSetMenuItems) };
-
-    NSString *testActionName = @"Test action";
-    auto customAction = adoptNS([[UIMenuItem alloc] initWithTitle:testActionName action:@selector(becomeFirstResponder)]);
-    [sharedMenuController setMenuItems:@[customAction.get()]];
-
     auto webView = createWebViewWithTextRecognitionEnhancements();
     auto inputDelegate = adoptNS([TestInputDelegate new]);
     [inputDelegate setFocusStartsInputSessionPolicyHandler:[](WKWebView *, id <_WKFocusedElementInfo>) {
@@ -296,27 +278,23 @@
     [webView objectByEvaluatingJavaScript:@"let image = document.images[0]; getSelection().setBaseAndExtent(image, 0, image, 1);"];
     [webView waitForNextPresentationUpdate];
 
-    auto hasMenuItemWithTitle = [&] (NSString *title) {
-        for (UIMenuItem *item in sharedMenuController.menuItems) {
-            if ([item.title isEqualToString:title])
-                return YES;
-        }
-        return NO;
-    };
+    auto menuBuilder = adoptNS([[TestUIMenuBuilder alloc] init]);
+    [webView buildMenuWithBuilder:menuBuilder.get()];
+    EXPECT_TRUE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTitleMarkupImage()]);
 
-    EXPECT_TRUE(hasMenuItemWithTitle(testActionName));
-    EXPECT_WK_STREQ(sharedMenuController.menuItems.firstObject.title, WebCore::contextMenuItemTitleMarkupImage());
-
     [webView selectAll:nil];
     [webView waitForNextPresentationUpdate];
 
-    EXPECT_TRUE(hasMenuItemWithTitle(testActionName));
-    EXPECT_FALSE(hasMenuItemWithTitle(WebCore::contextMenuItemTitleMarkupImage()));
+    [menuBuilder reset];
+    [webView buildMenuWithBuilder:menuBuilder.get()];
+    EXPECT_FALSE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTitleMarkupImage()]);
 
     [webView objectByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(document.body, 0, document.images[0], 1);"];
     [webView waitForNextPresentationUpdate];
-    EXPECT_TRUE(hasMenuItemWithTitle(testActionName));
-    EXPECT_WK_STREQ(sharedMenuController.menuItems.firstObject.title, WebCore::contextMenuItemTitleMarkupImage());
+
+    [menuBuilder reset];
+    [webView buildMenuWithBuilder:menuBuilder.get()];
+    EXPECT_TRUE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTitleMarkupImage()]);
 }
 
 #endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) && PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm (290449 => 290450)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm	2022-02-24 20:08:49 UTC (rev 290449)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKContentViewEditingActions.mm	2022-02-24 20:13:49 UTC (rev 290450)
@@ -32,9 +32,11 @@
 #import "Test.h"
 #import "TestInputDelegate.h"
 #import "TestNavigationDelegate.h"
+#import "TestUIMenuBuilder.h"
 #import "TestWKWebView.h"
 #import "UIKitSPI.h"
 #import "WKWebViewConfigurationExtras.h"
+#import <WebCore/LocalizedStrings.h>
 #import <WebKit/WKWebViewConfigurationPrivate.h>
 #import <WebKit/WebKit.h>
 #import <wtf/RetainPtr.h>
@@ -89,18 +91,19 @@
     [webView stringByEvaluatingJavaScript:@"selectImageOverlay()"];
     [webView waitForNextPresentationUpdate];
 
-    auto createHighlightForCurrentQuickNoteWithRangeSelector = NSSelectorFromString(@"createHighlightForCurrentQuickNoteWithRange:");
-    auto createHighlightForNewQuickNoteWithRangeSelector = NSSelectorFromString(@"createHighlightForNewQuickNoteWithRange:");
+    auto menuBuilder = adoptNS([[TestUIMenuBuilder alloc] init]);
+    [webView buildMenuWithBuilder:menuBuilder.get()];
+    EXPECT_FALSE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTagAddHighlightToNewQuickNote()]);
+    EXPECT_FALSE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTagAddHighlightToCurrentQuickNote()]);
 
-    auto contentView = [webView textInputContentView];
-    EXPECT_NULL([contentView targetForAction:createHighlightForCurrentQuickNoteWithRangeSelector withSender:nil]);
-    EXPECT_NULL([contentView targetForAction:createHighlightForNewQuickNoteWithRangeSelector withSender:nil]);
-
     [webView synchronouslyLoadTestPageNamed:@"simple"];
     [webView selectAll:nil];
     [webView waitForNextPresentationUpdate];
-    EXPECT_NULL([contentView targetForAction:createHighlightForCurrentQuickNoteWithRangeSelector withSender:nil]);
-    EXPECT_EQ([contentView targetForAction:createHighlightForNewQuickNoteWithRangeSelector withSender:nil], contentView);
+
+    [menuBuilder reset];
+    [webView buildMenuWithBuilder:menuBuilder.get()];
+    EXPECT_TRUE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTagAddHighlightToNewQuickNote()]);
+    EXPECT_FALSE([menuBuilder containsActionWithTitle:WebCore::contextMenuItemTagAddHighlightToCurrentQuickNote()]);
 }
 
 #endif // ENABLE(APP_HIGHLIGHTS)

Added: trunk/Tools/TestWebKitAPI/ios/TestUIMenuBuilder.h (0 => 290450)


--- trunk/Tools/TestWebKitAPI/ios/TestUIMenuBuilder.h	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/ios/TestUIMenuBuilder.h	2022-02-24 20:13:49 UTC (rev 290450)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if PLATFORM(IOS_FAMILY)
+
+#import <UIKit/UIKit.h>
+
+@interface TestUIMenuBuilder : NSObject<UIMenuBuilder>
+
+- (BOOL)containsActionWithTitle:(NSString *)title;
+- (void)reset;
+
+@end
+
+#endif // PLATFORM(IOS_FAMILY)

Added: trunk/Tools/TestWebKitAPI/ios/TestUIMenuBuilder.mm (0 => 290450)


--- trunk/Tools/TestWebKitAPI/ios/TestUIMenuBuilder.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/ios/TestUIMenuBuilder.mm	2022-02-24 20:13:49 UTC (rev 290450)
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "TestUIMenuBuilder.h"
+
+#import <wtf/RetainPtr.h>
+
+#if PLATFORM(IOS_FAMILY)
+
+@implementation TestUIMenuBuilder {
+    RetainPtr<NSMutableDictionary<UIMenuIdentifier, UIMenu *>> _menusByIdentifier;
+}
+
+- (instancetype)init
+{
+    if (!(self = [super init]))
+        return nil;
+
+    // FIXME: A flat dictionary containing all UIMenus is sufficient to test all of the
+    // non-default items currently supplied by WebKit. We'll want to augment this in the
+    // future if we need to test menu nesting or item ordering.
+    _menusByIdentifier = adoptNS([NSMutableDictionary<UIMenuIdentifier, UIMenu *> new]);
+    return self;
+}
+
+- (UIMenuSystem *)system
+{
+    return UIMenuSystem.mainSystem;
+}
+
+- (UIMenu *)menuForIdentifier:(UIMenuIdentifier)identifier
+{
+    return [_menusByIdentifier objectForKey:identifier];
+}
+
+- (UIMenuElement *)findMatching:(BOOL(^)(UIMenuElement *))isMatch
+{
+    for (UIMenu *menu in [_menusByIdentifier allValues]) {
+        for (UIMenuElement *child in menu.children) {
+            if (isMatch(child))
+                return child;
+        }
+    }
+    return nil;
+}
+
+- (BOOL)containsActionWithTitle:(NSString *)title
+{
+    return [self findMatching:^BOOL(UIMenuElement *element) {
+        return [dynamic_objc_cast<UIAction>(element).title isEqual:title];
+    }];
+}
+
+- (void)reset
+{
+    [_menusByIdentifier removeAllObjects];
+}
+
+- (UIAction *)actionForIdentifier:(UIActionIdentifier)identifier
+{
+    return (UIAction *)[self findMatching:^BOOL(UIMenuElement *element) {
+        return [dynamic_objc_cast<UIAction>(element).identifier isEqual:identifier];
+    }];
+}
+
+- (UICommand *)commandForAction:(SEL)action propertyList:(id)propertyList
+{
+    return (UICommand *)[self findMatching:^BOOL(UIMenuElement *element) {
+        auto command = dynamic_objc_cast<UICommand>(element);
+        return command.action == action && (command.propertyList == propertyList || [command.propertyList isEqual:propertyList]);
+    }];
+}
+
+- (void)replaceMenuForIdentifier:(UIMenuIdentifier)replacedIdentifier withMenu:(UIMenu *)replacementMenu
+{
+    [_menusByIdentifier setObject:replacementMenu forKey:replacedIdentifier];
+}
+
+- (void)replaceChildrenOfMenuForIdentifier:(UIMenuIdentifier)parentIdentifier fromChildrenBlock:(NSArray<UIMenuElement *> *(NS_NOESCAPE^)(NSArray<UIMenuElement *> *))childrenBlock
+{
+    if (auto menu = [self menuForIdentifier:parentIdentifier])
+        [_menusByIdentifier setObject:[menu menuByReplacingChildren:childrenBlock(menu.children)] forKey:parentIdentifier];
+}
+
+- (void)insertChildMenu:(UIMenu *)childMenu atStartOfMenuForIdentifier:(UIMenuIdentifier)parentIdentifier
+{
+    [self registerMenu:childMenu];
+}
+
+- (void)insertChildMenu:(UIMenu *)childMenu atEndOfMenuForIdentifier:(UIMenuIdentifier)parentIdentifier
+{
+    [self registerMenu:childMenu];
+}
+
+- (void)removeMenuForIdentifier:(UIMenuIdentifier)removedIdentifier
+{
+    [_menusByIdentifier removeObjectForKey:removedIdentifier];
+}
+
+- (void)insertSiblingMenu:(UIMenu *)siblingMenu beforeMenuForIdentifier:(UIMenuIdentifier)siblingIdentifier
+{
+    [self registerMenu:siblingMenu];
+}
+
+- (void)insertSiblingMenu:(UIMenu *)siblingMenu afterMenuForIdentifier:(UIMenuIdentifier)siblingIdentifier
+{
+    [self registerMenu:siblingMenu];
+}
+
+- (void)registerMenu:(UIMenu *)menu
+{
+    [_menusByIdentifier setObject:menu forKey:menu.identifier];
+}
+
+@end
+
+#endif // PLATFORM(IOS_FAMILY)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to