Changeset 238728 in webkit


Ignore:
Timestamp:
Nov 29, 2018 11:22:00 PM (5 years ago)
Author:
Wenson Hsieh
Message:

REGRESSION (r238635): Dragging a text selection within WKWebView causes the selection highlight to get into a bad state
https://bugs.webkit.org/show_bug.cgi?id=192165
<rdar://problem/46346682>

Reviewed by Daniel Bates.

Source/WebKit:

Fixes a bug in PageClientImpl::isViewFocused. Consider the following scenario:

  1. WKWebView is hosted within the view hierarchy
  2. First responder is *not* WKContentView
  3. The active focus retain count is nonzero

Before r238635, we would return true, due to condition (3). However, after r238635, we only consider whether the
first responder is WKContentView, since the web view is in the view hierarchy. This breaks scenarios where
WebKit or UIKit attempts to retain focus and later restore the content view to be the first responder (an
example of this is dragging a text selection between editable elements in the same web view).

To fix this, simply bail early and return true if focus is being retained.

  • UIProcess/ios/PageClientImplIOS.mm:

(WebKit::PageClientImpl::isViewFocused):

Tools:

Fixes 11 API tests that started failing or timing out after r238635. See below for more details.

  • TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEditActions.mm:

(TestWebKitAPI::webViewForEditActionTesting):
(TestWebKitAPI::webViewForEditActionTestingWithPageNamed):

Ensure that the web view becomes first responder before executing edit actions.

  • TestWebKitAPI/Tests/WebKitCocoa/autofocus-contenteditable.html:
  • TestWebKitAPI/Tests/WebKitCocoa/contenteditable-and-textarea.html:

Tweak these tests to allow selected content to overflow the width of the web view. Without this change,
ContentEditableToContentEditable and ContentEditableToTextarea will sometimes fail because the content causes
the body to scroll horizontally, so we miss the drop destination.

  • TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:

(loadTestPageAndEnsureInputSession):

Add a new helper to load a test page with a given name, become first responder, and wait until an input session
starts. Use this in various drag and drop tests to reduce code duplication.

  • TestWebKitAPI/cocoa/DragAndDropSimulator.h:
  • TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm:

(-[DragAndDropSimulator initWithWebView:]):
(-[DragAndDropSimulator _resetSimulatedState]):
(-[DragAndDropSimulator _concludeDropAndPerformOperationIfNecessary]):
(-[DragAndDropSimulator _advanceProgress]):

To more accurately emulate UIKit behavior, begin focus preservation when starting a drag, and attempt to clear
the focus preservation token when the drag session ends. This allows us to simulate and test the scenario that
regressed with r238635.

(-[DragAndDropSimulator ensureInputSession]):
(-[DragAndDropSimulator _webView:didStartInputSession:]):
(-[DragAndDropSimulator waitForInputSession]): Deleted.

Refactored into -ensureInputSession. Instead of assuming that an input session has not yet been started, simply
wait for an input session to start if needed.

  • TestWebKitAPI/ios/UIKitSPI.h:

Add a new SPI declaration.

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r238726 r238728  
     12018-11-29  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        REGRESSION (r238635): Dragging a text selection within WKWebView causes the selection highlight to get into a bad state
     4        https://bugs.webkit.org/show_bug.cgi?id=192165
     5        <rdar://problem/46346682>
     6
     7        Reviewed by Daniel Bates.
     8
     9        Fixes a bug in PageClientImpl::isViewFocused. Consider the following scenario:
     10        1. WKWebView is hosted within the view hierarchy
     11        2. First responder is *not* WKContentView
     12        3. The active focus retain count is nonzero
     13
     14        Before r238635, we would return true, due to condition (3). However, after r238635, we only consider whether the
     15        first responder is WKContentView, since the web view is in the view hierarchy. This breaks scenarios where
     16        WebKit or UIKit attempts to retain focus and later restore the content view to be the first responder (an
     17        example of this is dragging a text selection between editable elements in the same web view).
     18
     19        To fix this, simply bail early and return true if focus is being retained.
     20
     21        * UIProcess/ios/PageClientImplIOS.mm:
     22        (WebKit::PageClientImpl::isViewFocused):
     23
    1242018-11-29  Tim Horton  <timothy_horton@apple.com>
    225
  • trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm

    r238635 r238728  
    167167bool PageClientImpl::isViewFocused()
    168168{
    169     if (isViewInWindow() && ![m_webView _isBackground])
    170         return [m_webView _contentViewIsFirstResponder];
    171     return [m_webView _isRetainingActiveFocusedState];
     169    return (isViewInWindow() && ![m_webView _isBackground] && [m_webView _contentViewIsFirstResponder]) || [m_webView _isRetainingActiveFocusedState];
    172170}
    173171
  • trunk/Tools/ChangeLog

    r238726 r238728  
     12018-11-29  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        REGRESSION (r238635): Dragging a text selection within WKWebView causes the selection highlight to get into a bad state
     4        https://bugs.webkit.org/show_bug.cgi?id=192165
     5        <rdar://problem/46346682>
     6
     7        Reviewed by Daniel Bates.
     8
     9        Fixes 11 API tests that started failing or timing out after r238635. See below for more details.
     10
     11        * TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEditActions.mm:
     12        (TestWebKitAPI::webViewForEditActionTesting):
     13        (TestWebKitAPI::webViewForEditActionTestingWithPageNamed):
     14
     15        Ensure that the web view becomes first responder before executing edit actions.
     16
     17        * TestWebKitAPI/Tests/WebKitCocoa/autofocus-contenteditable.html:
     18        * TestWebKitAPI/Tests/WebKitCocoa/contenteditable-and-textarea.html:
     19
     20        Tweak these tests to allow selected content to overflow the width of the web view. Without this change,
     21        ContentEditableToContentEditable and ContentEditableToTextarea will sometimes fail because the content causes
     22        the body to scroll horizontally, so we miss the drop destination.
     23
     24        * TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:
     25        (loadTestPageAndEnsureInputSession):
     26
     27        Add a new helper to load a test page with a given name, become first responder, and wait until an input session
     28        starts. Use this in various drag and drop tests to reduce code duplication.
     29
     30        * TestWebKitAPI/cocoa/DragAndDropSimulator.h:
     31        * TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm:
     32        (-[DragAndDropSimulator initWithWebView:]):
     33        (-[DragAndDropSimulator _resetSimulatedState]):
     34        (-[DragAndDropSimulator _concludeDropAndPerformOperationIfNecessary]):
     35        (-[DragAndDropSimulator _advanceProgress]):
     36
     37        To more accurately emulate UIKit behavior, begin focus preservation when starting a drag, and attempt to clear
     38        the focus preservation token when the drag session ends. This allows us to simulate and test the scenario that
     39        regressed with r238635.
     40
     41        (-[DragAndDropSimulator ensureInputSession]):
     42        (-[DragAndDropSimulator _webView:didStartInputSession:]):
     43        (-[DragAndDropSimulator waitForInputSession]): Deleted.
     44
     45        Refactored into -ensureInputSession. Instead of assuming that an input session has not yet been started, simply
     46        wait for an input session to start if needed.
     47
     48        * TestWebKitAPI/ios/UIKitSPI.h:
     49
     50        Add a new SPI declaration.
     51
    1522018-11-29  Tim Horton  <timothy_horton@apple.com>
    253
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEditActions.mm

    r238186 r238728  
    7979    [webView synchronouslyLoadHTMLString:markup];
    8080    [webView _setEditable:YES];
     81    [webView becomeFirstResponder];
    8182    [webView stringByEvaluatingJavaScript:@"getSelection().setPosition(document.body, 1)"];
    8283    return webView;
     
    8889    [webView synchronouslyLoadTestPageNamed:testPageName];
    8990    [webView _setEditable:YES];
     91    [webView becomeFirstResponder];
    9092    [webView stringByEvaluatingJavaScript:@"getSelection().setPosition(document.body, 1)"];
    9193    return webView;
     
    244246{
    245247    auto source = webViewForEditActionTesting();
    246     auto destination = webViewForEditActionTesting(@"<div><br></div>");
    247 
    248248    [source selectAll:nil];
    249249    [source evaluateJavaScript:@"document.execCommand('bold'); document.execCommand('underline'); document.execCommand('italic')" completionHandler:nil];
    250250    [source _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
    251251
     252    auto destination = webViewForEditActionTesting(@"<div><br></div>");
    252253    [destination _pasteAndMatchStyle:nil];
    253254    [destination selectAll:nil];
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/autofocus-contenteditable.html

    r218433 r238728  
    1313            font-size: 200px;
    1414            white-space: nowrap;
     15        }
     16
     17        #source {
     18            overflow: hidden;
    1519        }
    1620
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/contenteditable-and-textarea.html

    r218433 r238728  
    1313            font-size: 200px;
    1414            white-space: nowrap;
     15        }
     16
     17        #source {
     18            overflow: hidden;
    1519        }
    1620
  • trunk/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm

    r238360 r238728  
    9999@end
    100100
     101static void loadTestPageAndEnsureInputSession(DragAndDropSimulator *simulator, NSString *testPageName)
     102{
     103    TestWKWebView *webView = [simulator webView];
     104    simulator.allowsFocusToStartInputSession = YES;
     105    [webView becomeFirstResponder];
     106    [webView synchronouslyLoadTestPageNamed:testPageName];
     107    [simulator ensureInputSession];
     108}
     109
    101110static NSValue *makeCGRectValue(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
    102111{
     
    342351    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
    343352
    344     [webView loadTestPageNamed:@"autofocus-contenteditable"];
    345     [simulator waitForInputSession];
     353    loadTestPageAndEnsureInputSession(simulator.get(), @"autofocus-contenteditable");
    346354    [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
    347355
     
    363371    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
    364372
    365     [webView loadTestPageNamed:@"contenteditable-and-textarea"];
    366     [simulator waitForInputSession];
     373    loadTestPageAndEnsureInputSession(simulator.get(), @"contenteditable-and-textarea");
    367374    [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
    368375
     
    395402    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
    396403
    397     [webView loadTestPageNamed:@"two-paragraph-contenteditable"];
    398     [simulator waitForInputSession];
     404    loadTestPageAndEnsureInputSession(simulator.get(), @"two-paragraph-contenteditable");
    399405    [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(250, 450)];
    400406
     
    426432    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
    427433
    428     [webView loadTestPageNamed:@"textarea-to-input"];
    429     [simulator waitForInputSession];
     434    loadTestPageAndEnsureInputSession(simulator.get(), @"textarea-to-input");
    430435    [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)];
    431436
     
    441446    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
    442447
    443     [webView loadTestPageNamed:@"textarea-to-input"];
    444     [simulator waitForInputSession];
     448    loadTestPageAndEnsureInputSession(simulator.get(), @"textarea-to-input");
    445449    [webView stringByEvaluatingJavaScript:@"source.value = 'pneumonoultramicroscopicsilicovolcanoconiosis'"];
    446450    [webView stringByEvaluatingJavaScript:@"source.selectionStart = 0"];
     
    464468    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
    465469
    466     [webView loadTestPageNamed:@"textarea-to-input"];
    467     [simulator waitForInputSession];
     470    loadTestPageAndEnsureInputSession(simulator.get(), @"textarea-to-input");
    468471    [webView stringByEvaluatingJavaScript:@"source.value = 'https://webkit.org/'"];
    469472    [webView stringByEvaluatingJavaScript:@"source.selectionStart = 0"];
  • trunk/Tools/TestWebKitAPI/cocoa/DragAndDropSimulator.h

    r238360 r238728  
    9090- (instancetype)initWithWebView:(TestWKWebView *)webView;
    9191- (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation additionalItemRequestLocations:(ProgressToCGPointValueMap)additionalItemRequestLocations;
    92 - (void)waitForInputSession;
     92- (void)ensureInputSession;
    9393
    9494@property (nonatomic, readonly) DragAndDropPhase phase;
  • trunk/Tools/TestWebKitAPI/ios/DragAndDropSimulatorIOS.mm

    r238360 r238728  
    311311    RetainPtr<NSMutableArray<_WKAttachment *>> _removedAttachments;
    312312
    313     bool _isDoneWaitingForInputSession;
     313    bool _hasStartedInputSession;
    314314    double _currentProgress;
    315315    bool _isDoneWithCurrentRun;
     
    339339        _shouldEnsureUIApplication = NO;
    340340        _shouldAllowMoveOperation = YES;
    341         _isDoneWaitingForInputSession = true;
    342341        [_webView setUIDelegate:self];
    343342        [_webView _setInputDelegate:self];
     
    374373    _queuedAdditionalItemRequestLocations = adoptNS([[NSMutableArray alloc] init]);
    375374    _liftPreviews = adoptNS([[NSMutableArray alloc] init]);
     375    _hasStartedInputSession = false;
    376376}
    377377
     
    460460    [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidEnd:_dropSession.get()];
    461461
    462     if (_dragSession)
    463         [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:operation];
     462    if (_dragSession) {
     463        auto delegate = [_webView dragInteractionDelegate];
     464        [delegate dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:operation];
     465        if ([delegate respondsToSelector:@selector(_clearToken:)])
     466            [(id <UITextInputMultiDocument>)delegate _clearToken:nil];
     467        [_webView becomeFirstResponder];
     468    }
    464469}
    465470
     
    544549        }
    545550
    546         [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] sessionWillBegin:_dragSession.get()];
     551        auto delegate = [_webView dragInteractionDelegate];
     552        if ([delegate respondsToSelector:@selector(_preserveFocusWithToken:destructively:)])
     553            [(id <UITextInputMultiDocument>)delegate _preserveFocusWithToken:nil destructively:NO];
     554
     555        [_webView resignFirstResponder];
     556
     557        [delegate dragInteraction:[_webView dragInteraction] sessionWillBegin:_dragSession.get()];
    547558
    548559        RetainPtr<WKWebView> retainedWebView = _webView;
     
    619630}
    620631
    621 - (void)waitForInputSession
    622 {
    623     _isDoneWaitingForInputSession = false;
    624 
    625     // Waiting for an input session implies that we should allow input sessions to begin.
    626     self.allowsFocusToStartInputSession = YES;
    627 
    628     Util::run(&_isDoneWaitingForInputSession);
     632- (void)ensureInputSession
     633{
     634    Util::run(&_hasStartedInputSession);
    629635}
    630636
     
    708714- (void)_webView:(WKWebView *)webView didStartInputSession:(id <_WKFormInputSession>)inputSession
    709715{
    710     _isDoneWaitingForInputSession = true;
     716    _hasStartedInputSession = true;
    711717}
    712718
  • trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h

    r238383 r238728  
    8484- (void)_preserveFocusWithToken:(id <NSCopying, NSSecureCoding>)token destructively:(BOOL)destructively;
    8585- (BOOL)_restoreFocusWithToken:(id <NSCopying, NSSecureCoding>)token;
     86- (void)_clearToken:(id <NSCopying, NSSecureCoding>)token;
    8687@end
    8788
Note: See TracChangeset for help on using the changeset viewer.