Title: [281054] trunk
Revision
281054
Author
wenson_hs...@apple.com
Date
2021-08-13 17:47:03 -0700 (Fri, 13 Aug 2021)

Log Message

[iOS 15] fast/events/touch/ios/long-press-on-link.html is a constant crash
https://bugs.webkit.org/show_bug.cgi?id=229095
rdar://80386326

Reviewed by Tim Horton.

Source/WebKit:

This test crashes when run immediately after another test that attempts to present the context menu and ends
with the context menu still showing (in this case, fast/events/touch/ios/long-press-on-image.html). Running
these tests back to back causes us to immediately dismiss the context menu interaction while transitioning to
the second test, which triggers the context menu interaction's dismissal animation.

This animation ends in the middle of the next test (long-press-on-link.html), after the long press has begun and
the context menu interaction has already requested a targeted hint preview, but (importantly) before the context
menu peek animation actually begins (i.e. `-contextMenuInteraction:willDisplayMenuForConfiguration:animator:`).
As a result, we tear down the hint container too early (with the hint view still in the view hierarchy), and
subsequently crash with an exception when UIKit tries to perform coordinate space conversions with the now-
unparented view.

To fix this, we make two small adjustments:
1.      Avoid unparenting the context menu hint container if it still has subviews.
2.      Add plumbing to ensure that the context menu hint container is unparented once all of its subviews have
        been removed (and the container isn't being used for anything else).

* Platform/spi/ios/UIKitSPI.h:

Add an SPI declaration for the `-_didRemoveSubview:` subclassing hook.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKTargetedPreviewContainer initWithContentView:]):
(-[WKTargetedPreviewContainer _didRemoveSubview:]):

Add a new subclass for WKTargetedPreviewContainer that notifies WKContentView when its last subview has been
removed, such that WKContentView can tear down the container view if needed.

(-[WKContentView cleanUpInteraction]):
(-[WKContentView removeContextMenuViewIfPossibleForActionSheetAssistant:]):
(-[WKContentView _targetedPreviewContainerDidRemoveLastSubview:]):
(-[WKContentView _createPreviewContainerWithLayerName:]):
(-[WKContentView _removeContextMenuHintContainerIfPossible]):
(-[WKContentView contextMenuInteraction:willEndForConfiguration:animator:]):
(-[WKContentView _removeContextMenuViewIfPossible]): Deleted.

Additionally rename `-_removeContextMenuViewIfPossible` to `-_removeContextMenuHintContainerIfPossible`, to make
it clear that this method is about removing the container view for context menu hints (and not the hints
themselves).

* UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm:
(-[WKDataListSuggestionsDropdown _removeContextMenuInteraction]):
* UIProcess/ios/forms/WKDateTimeInputControl.mm:
(-[WKDateTimePicker removeContextMenuInteraction]):
* UIProcess/ios/forms/WKFileUploadPanel.mm:
(-[WKFileUploadPanel removeContextMenuInteraction]):
* UIProcess/ios/forms/WKFormSelectPicker.mm:
(-[WKSelectPicker removeContextMenuInteraction]):

LayoutTests:

Remove the failing test expectation (and remove a passing expectation for iOS 14 which is no longer necessary
after this fix).

* platform/ios-14/TestExpectations:
* platform/ios/TestExpectations:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (281053 => 281054)


--- trunk/LayoutTests/ChangeLog	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/LayoutTests/ChangeLog	2021-08-14 00:47:03 UTC (rev 281054)
@@ -1,3 +1,17 @@
+2021-08-13  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS 15] fast/events/touch/ios/long-press-on-link.html is a constant crash
+        https://bugs.webkit.org/show_bug.cgi?id=229095
+        rdar://80386326
+
+        Reviewed by Tim Horton.
+
+        Remove the failing test expectation (and remove a passing expectation for iOS 14 which is no longer necessary
+        after this fix).
+
+        * platform/ios-14/TestExpectations:
+        * platform/ios/TestExpectations:
+
 2021-08-13  Ayumi Kojima  <ayumi_koj...@apple.com>
 
         [ Win EWS ] http/tests/misc/webtiming-slow-load.py is failing.

Modified: trunk/LayoutTests/platform/ios/TestExpectations (281053 => 281054)


--- trunk/LayoutTests/platform/ios/TestExpectations	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/LayoutTests/platform/ios/TestExpectations	2021-08-14 00:47:03 UTC (rev 281054)
@@ -3410,9 +3410,6 @@
 # rdar://80395949 ([ iOS15 ] http/tests/websocket/tests/hybi/too-long-payload.html is a constant timeout)
 http/tests/websocket/tests/hybi/too-long-payload.html [ Timeout ]
 
-#rdar://80386326 ([ iOS15 Release ] fast/events/touch/ios/long-press-on-link.html is a constant crash)
-fast/events/touch/ios/long-press-on-link.html [ Crash ]
-
 # rdar://80392337 ([ iOS15 ] fast/text/international/system-language/navigator-language/navigator-language-ru.html is a constant failure)
 fast/text/international/system-language/navigator-language/navigator-language-ru.html [ Failure ]
 

Modified: trunk/LayoutTests/platform/ios-14/TestExpectations (281053 => 281054)


--- trunk/LayoutTests/platform/ios-14/TestExpectations	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/LayoutTests/platform/ios-14/TestExpectations	2021-08-14 00:47:03 UTC (rev 281054)
@@ -72,9 +72,6 @@
 # rdar://80395949 ([ iOS15 ] http/tests/websocket/tests/hybi/too-long-payload.html is a constant timeout)
 http/tests/websocket/tests/hybi/too-long-payload.html [ Pass ]
 
-#rdar://80386326 ([ iOS15 Release ] fast/events/touch/ios/long-press-on-link.html is a constant crash)
-fast/events/touch/ios/long-press-on-link.html [ Pass ]
-
 # rdar://80392337 ([ iOS15 ] fast/text/international/system-language/navigator-language/navigator-language-ru.html is a constant failure)
 fast/text/international/system-language/navigator-language/navigator-language-ru.html [ Pass ]
 

Modified: trunk/Source/WebKit/ChangeLog (281053 => 281054)


--- trunk/Source/WebKit/ChangeLog	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/ChangeLog	2021-08-14 00:47:03 UTC (rev 281054)
@@ -1,3 +1,61 @@
+2021-08-13  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS 15] fast/events/touch/ios/long-press-on-link.html is a constant crash
+        https://bugs.webkit.org/show_bug.cgi?id=229095
+        rdar://80386326
+
+        Reviewed by Tim Horton.
+
+        This test crashes when run immediately after another test that attempts to present the context menu and ends
+        with the context menu still showing (in this case, fast/events/touch/ios/long-press-on-image.html). Running
+        these tests back to back causes us to immediately dismiss the context menu interaction while transitioning to
+        the second test, which triggers the context menu interaction's dismissal animation.
+
+        This animation ends in the middle of the next test (long-press-on-link.html), after the long press has begun and
+        the context menu interaction has already requested a targeted hint preview, but (importantly) before the context
+        menu peek animation actually begins (i.e. `-contextMenuInteraction:willDisplayMenuForConfiguration:animator:`).
+        As a result, we tear down the hint container too early (with the hint view still in the view hierarchy), and
+        subsequently crash with an exception when UIKit tries to perform coordinate space conversions with the now-
+        unparented view.
+
+        To fix this, we make two small adjustments:
+        1.      Avoid unparenting the context menu hint container if it still has subviews.
+        2.      Add plumbing to ensure that the context menu hint container is unparented once all of its subviews have
+                been removed (and the container isn't being used for anything else).
+
+        * Platform/spi/ios/UIKitSPI.h:
+
+        Add an SPI declaration for the `-_didRemoveSubview:` subclassing hook.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKTargetedPreviewContainer initWithContentView:]):
+        (-[WKTargetedPreviewContainer _didRemoveSubview:]):
+
+        Add a new subclass for WKTargetedPreviewContainer that notifies WKContentView when its last subview has been
+        removed, such that WKContentView can tear down the container view if needed.
+
+        (-[WKContentView cleanUpInteraction]):
+        (-[WKContentView removeContextMenuViewIfPossibleForActionSheetAssistant:]):
+        (-[WKContentView _targetedPreviewContainerDidRemoveLastSubview:]):
+        (-[WKContentView _createPreviewContainerWithLayerName:]):
+        (-[WKContentView _removeContextMenuHintContainerIfPossible]):
+        (-[WKContentView contextMenuInteraction:willEndForConfiguration:animator:]):
+        (-[WKContentView _removeContextMenuViewIfPossible]): Deleted.
+
+        Additionally rename `-_removeContextMenuViewIfPossible` to `-_removeContextMenuHintContainerIfPossible`, to make
+        it clear that this method is about removing the container view for context menu hints (and not the hints
+        themselves).
+
+        * UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm:
+        (-[WKDataListSuggestionsDropdown _removeContextMenuInteraction]):
+        * UIProcess/ios/forms/WKDateTimeInputControl.mm:
+        (-[WKDateTimePicker removeContextMenuInteraction]):
+        * UIProcess/ios/forms/WKFileUploadPanel.mm:
+        (-[WKFileUploadPanel removeContextMenuInteraction]):
+        * UIProcess/ios/forms/WKFormSelectPicker.mm:
+        (-[WKSelectPicker removeContextMenuInteraction]):
+
 2021-08-13  Jer Noble  <jer.no...@apple.com>
 
         [Cocoa] RemoteMediaPlayerProxy does not receive acceleratedRenderingStateChanged() when video element switches sources

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (281053 => 281054)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2021-08-14 00:47:03 UTC (rev 281054)
@@ -637,6 +637,7 @@
 @property (nonatomic, setter=_setContinuousCornerRadius:) CGFloat _continuousCornerRadius;
 - (void)insertSubview:(UIView *)view above:(UIView *)sibling;
 - (void)viewWillMoveToSuperview:(UIView *)newSuperview;
+- (void)_didRemoveSubview:(UIView *)subview;
 - (CGSize)convertSize:(CGSize)size toView:(UIView *)view;
 - (void)_removeAllAnimations:(BOOL)includeSubviews;
 - (UIColor *)_inheritedInteractionTintColor;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (281053 => 281054)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-08-14 00:47:03 UTC (rev 281054)
@@ -122,6 +122,7 @@
 @class WKImageAnalysisGestureRecognizer;
 @class WKMouseGestureRecognizer;
 @class WKInspectorNodeSearchGestureRecognizer;
+@class WKTargetedPreviewContainer;
 @class WKTextRange;
 @class _WKTextInputContext;
 
@@ -318,11 +319,11 @@
     RetainPtr<UIWebFormAccessory> _formAccessoryView;
     RetainPtr<_UIHighlightView> _highlightView;
     RetainPtr<UIView> _interactionViewsContainerView;
-    RetainPtr<UIView> _contextMenuHintContainerView;
+    RetainPtr<WKTargetedPreviewContainer> _contextMenuHintContainerView;
     WeakObjCPtr<UIScrollView> _scrollViewForTargetedPreview;
     CGPoint _scrollViewForTargetedPreviewInitialOffset;
-    RetainPtr<UIView> _dragPreviewContainerView;
-    RetainPtr<UIView> _dropPreviewContainerView;
+    RetainPtr<WKTargetedPreviewContainer> _dragPreviewContainerView;
+    RetainPtr<WKTargetedPreviewContainer> _dropPreviewContainerView;
     RetainPtr<NSString> _markedText;
     RetainPtr<WKActionSheetAssistant> _actionSheetAssistant;
 #if ENABLE(AIRPLAY_PICKER)
@@ -731,7 +732,8 @@
 
 - (UITargetedPreview *)_createTargetedContextMenuHintPreviewForFocusedElement;
 - (UITargetedPreview *)_createTargetedContextMenuHintPreviewIfPossible;
-- (void)_removeContextMenuViewIfPossible;
+- (void)_removeContextMenuHintContainerIfPossible;
+- (void)_targetedPreviewContainerDidRemoveLastSubview:(WKTargetedPreviewContainer *)containerView;
 #endif
 
 #if ENABLE(ATTACHMENT_ELEMENT)

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (281053 => 281054)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-08-14 00:47:03 UTC (rev 281054)
@@ -768,6 +768,39 @@
 
 @end
 
+@interface WKTargetedPreviewContainer : UIView
+- (instancetype)initWithContentView:(WKContentView *)contentView NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
+@end
+
+@implementation WKTargetedPreviewContainer {
+    __weak WKContentView *_contentView;
+}
+
+- (instancetype)initWithContentView:(WKContentView *)contentView
+{
+    if (!(self = [super initWithFrame:CGRectZero]))
+        return nil;
+
+    _contentView = contentView;
+    return self;
+}
+
+- (void)_didRemoveSubview:(UIView *)subview
+{
+    [super _didRemoveSubview:subview];
+
+    if (self.subviews.count)
+        return;
+
+#if USE(UICONTEXTMENU)
+    [_contentView _targetedPreviewContainerDidRemoveLastSubview:self];
+#endif
+}
+
+@end
+
 @interface WKContentView (WKInteractionPrivate)
 - (void)accessibilitySpeakSelectionSetContent:(NSString *)string;
 - (NSArray *)webSelectionRectsForSelectionGeometries:(const Vector<WebCore::SelectionGeometry>&)selectionRects;
@@ -1140,7 +1173,7 @@
     _layerTreeTransactionIdAtLastInteractionStart = { };
 
 #if USE(UICONTEXTMENU)
-    [self _removeContextMenuViewIfPossible];
+    [self _removeContextMenuHintContainerIfPossible];
 #endif // USE(UICONTEXTMENU)
 
 #if ENABLE(DRAG_SUPPORT)
@@ -7756,7 +7789,7 @@
 
 - (void)removeContextMenuViewIfPossibleForActionSheetAssistant:(WKActionSheetAssistant *)assistant
 {
-    [self _removeContextMenuViewIfPossible];
+    [self _removeContextMenuHintContainerIfPossible];
 }
 
 - (void)actionSheetAssistantDidShowContextMenu:(WKActionSheetAssistant *)assistant
@@ -7769,6 +7802,12 @@
     [_webView _didDismissContextMenu];
 }
 
+- (void)_targetedPreviewContainerDidRemoveLastSubview:(WKTargetedPreviewContainer *)containerView
+{
+    if (_contextMenuHintContainerView == containerView)
+        [self _removeContextMenuHintContainerIfPossible];
+}
+
 #endif // USE(UICONTEXTMENU)
 
 - (BOOL)_shouldUseContextMenus
@@ -7857,9 +7896,9 @@
 
 #endif // HAVE(PASTEBOARD_DATA_OWNER)
 
-- (RetainPtr<UIView>)_createPreviewContainerWithLayerName:(NSString *)layerName
+- (RetainPtr<WKTargetedPreviewContainer>)_createPreviewContainerWithLayerName:(NSString *)layerName
 {
-    auto container = adoptNS([[UIView alloc] init]);
+    auto container = adoptNS([[WKTargetedPreviewContainer alloc] initWithContentView:self]);
     [container layer].anchorPoint = CGPointZero;
     [container layer].name = layerName;
     return container;
@@ -8696,7 +8735,7 @@
     return _contextMenuInteractionTargetedPreview.get();
 }
 
-- (void)_removeContextMenuViewIfPossible
+- (void)_removeContextMenuHintContainerIfPossible
 {
 #if HAVE(LINK_PREVIEW)
     // If a new _contextMenuElementInfo is installed, we've started another interaction,
@@ -8712,7 +8751,7 @@
     // and for the file upload panel...
     if (_fileUploadPanel)
         return;
-    
+
     // and for the date/time picker.
     if ([self dateTimeInputControl])
         return;
@@ -8719,7 +8758,10 @@
 
     if ([self selectControl])
         return;
-    
+
+    if ([_contextMenuHintContainerView subviews].count)
+        return;
+
     [self _removeContainerForContextMenuHintPreviews];
 }
 
@@ -11382,7 +11424,7 @@
         auto strongSelf = weakSelf.get();
         if (!strongSelf)
             return;
-        [strongSelf _removeContextMenuViewIfPossible];
+        [strongSelf _removeContextMenuHintContainerIfPossible];
         [strongSelf->_webView _didDismissContextMenu];
     }];
 }

Modified: trunk/Source/WebKit/UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm (281053 => 281054)


--- trunk/Source/WebKit/UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/UIProcess/ios/WebDataListSuggestionsDropdownIOS.mm	2021-08-14 00:47:03 UTC (rev 281054)
@@ -516,7 +516,7 @@
 
     [self.view removeInteraction:_suggestionsContextMenuInteraction.get()];
     _suggestionsContextMenuInteraction = nil;
-    [self.view _removeContextMenuViewIfPossible];
+    [self.view _removeContextMenuHintContainerIfPossible];
     [self.view.webView _didDismissContextMenu];
 }
 

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKDateTimeInputControl.mm (281053 => 281054)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKDateTimeInputControl.mm	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKDateTimeInputControl.mm	2021-08-14 00:47:03 UTC (rev 281054)
@@ -469,7 +469,7 @@
     if (_dateTimeContextMenuInteraction) {
         [_view removeInteraction:_dateTimeContextMenuInteraction.get()];
         _dateTimeContextMenuInteraction = nil;
-        [_view _removeContextMenuViewIfPossible];
+        [_view _removeContextMenuHintContainerIfPossible];
         [_view.webView _didDismissContextMenu];
     }
 }

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKFileUploadPanel.mm (281053 => 281054)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKFileUploadPanel.mm	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKFileUploadPanel.mm	2021-08-14 00:47:03 UTC (rev 281054)
@@ -496,7 +496,7 @@
     if (_documentContextMenuInteraction) {
         [_view removeInteraction:_documentContextMenuInteraction.get()];
         _documentContextMenuInteraction = nil;
-        [_view _removeContextMenuViewIfPossible];
+        [_view _removeContextMenuHintContainerIfPossible];
     }
 }
 

Modified: trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.mm (281053 => 281054)


--- trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.mm	2021-08-14 00:35:33 UTC (rev 281053)
+++ trunk/Source/WebKit/UIProcess/ios/forms/WKFormSelectPicker.mm	2021-08-14 00:47:03 UTC (rev 281054)
@@ -701,7 +701,7 @@
 
     [_view removeInteraction:_selectContextMenuInteraction.get()];
     _selectContextMenuInteraction = nil;
-    [_view _removeContextMenuViewIfPossible];
+    [_view _removeContextMenuHintContainerIfPossible];
     [_view.webView _didDismissContextMenu];
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to