Changeset 261398 in webkit


Ignore:
Timestamp:
May 8, 2020, 10:57:39 AM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS] caret appears in the middle of a search field when field is focused on agoda.com
https://bugs.webkit.org/show_bug.cgi?id=211591
<rdar://problem/60605873>

Reviewed by Antoine Quint.

Source/WebCore:

See WebKit/ChangeLog for more details.

Test: editing/selection/ios/caret-rect-after-animating-focused-text-field.html

  • animation/WebAnimation.cpp:

(WebCore::WebAnimation::finishNotificationSteps):

Add plumbing to call out to the WebKit client layer after an animation finishes running.

  • dom/Node.h:
  • editing/VisibleSelection.h:

WEBCORE_EXPORT a couple of methods.

  • page/ChromeClient.h:

(WebCore::ChromeClient::animationDidFinishForElement):

Source/WebKit:

The main search field on the mobile version of this website begins offscreen, with a CSS transform that moves it
to the far right; tapping the button element that (visually) has a search-field-like appearance on the front
page programmatically focuses the real offscreen search field, and animates it onscreen by changing the CSS
transform attribute to remove the x-axis translation.

On iOS, the caret rect is computed and sent to the UI process via editor state updates; however, the editor
state is computed immediately after focusing the input field. As such, the caret rect at this moment is computed
in the middle of the animation, leaving it stuck in an unpredictable location.

To fix this, add plumbing to call into the WebKit client layer when an animation has ended. On iOS, if the
selection is visible (i.e. a ranged selection, or editable caret), then check to see whether the element that
has finished animating contains either endpoint of the selection. If so, then schedule a followup editor state
update to push updated selection information to the UI process.

  • WebProcess/WebCoreSupport/WebChromeClient.cpp:

(WebKit::WebChromeClient::animationDidFinishForElement):

  • WebProcess/WebCoreSupport/WebChromeClient.h:

Add a new client hook for when animations end.

  • WebProcess/WebPage/WebPage.cpp:

(WebKit::WebPage::animationDidFinishForElement):

  • WebProcess/WebPage/WebPage.h:
  • WebProcess/WebPage/ios/WebPageIOS.mm:

Add logic to schedule a new editor state update if needed, after an animation ends that might affect either
the start or end of the selection.

(WebKit::WebPage::animationDidFinishForElement):

LayoutTests:

Add a new layout test to verify that the caret view eventually becomes visible when after a focused text field
containing the caret is animated.

  • editing/selection/ios/caret-rect-after-animating-focused-text-field-expected.txt: Added.
  • editing/selection/ios/caret-rect-after-animating-focused-text-field.html: Added.
Location:
trunk
Files:
2 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r261395 r261398  
     12020-05-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] caret appears in the middle of a search field when field is focused on agoda.com
     4        https://bugs.webkit.org/show_bug.cgi?id=211591
     5        <rdar://problem/60605873>
     6
     7        Reviewed by Antoine Quint.
     8
     9        Add a new layout test to verify that the caret view eventually becomes visible when after a focused text field
     10        containing the caret is animated.
     11
     12        * editing/selection/ios/caret-rect-after-animating-focused-text-field-expected.txt: Added.
     13        * editing/selection/ios/caret-rect-after-animating-focused-text-field.html: Added.
     14
    1152020-05-08  Wenson Hsieh  <wenson_hsieh@apple.com>
    216
  • trunk/Source/WebCore/ChangeLog

    r261395 r261398  
     12020-05-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] caret appears in the middle of a search field when field is focused on agoda.com
     4        https://bugs.webkit.org/show_bug.cgi?id=211591
     5        <rdar://problem/60605873>
     6
     7        Reviewed by Antoine Quint.
     8
     9        See WebKit/ChangeLog for more details.
     10
     11        Test: editing/selection/ios/caret-rect-after-animating-focused-text-field.html
     12
     13        * animation/WebAnimation.cpp:
     14        (WebCore::WebAnimation::finishNotificationSteps):
     15
     16        Add plumbing to call out to the WebKit client layer after an animation finishes running.
     17
     18        * dom/Node.h:
     19        * editing/VisibleSelection.h:
     20
     21        WEBCORE_EXPORT a couple of methods.
     22
     23        * page/ChromeClient.h:
     24        (WebCore::ChromeClient::animationDidFinishForElement):
     25
    1262020-05-08  Wenson Hsieh  <wenson_hsieh@apple.com>
    227
  • trunk/Source/WebCore/animation/WebAnimation.cpp

    r260671 r261398  
    3131#include "AnimationTimeline.h"
    3232#include "CSSComputedStyleDeclaration.h"
     33#include "Chrome.h"
     34#include "ChromeClient.h"
    3335#include "DOMPromiseProxy.h"
    3436#include "DeclarativeAnimation.h"
    3537#include "Document.h"
    3638#include "DocumentTimeline.h"
     39#include "Element.h"
    3740#include "EventLoop.h"
    3841#include "EventNames.h"
     
    905908    //    Otherwise, queue a task to dispatch finishEvent at animation. The task source for this task is the DOM manipulation task source.
    906909    enqueueAnimationPlaybackEvent(eventNames().finishEvent, currentTime(), m_timeline ? m_timeline->currentTime() : WTF::nullopt);
     910
     911    if (is<KeyframeEffect>(m_effect)) {
     912        if (auto target = makeRefPtr(downcast<KeyframeEffect>(*m_effect).target())) {
     913            if (auto* page = target->document().page())
     914                page->chrome().client().animationDidFinishForElement(*target);
     915        }
     916    }
    907917}
    908918
  • trunk/Source/WebCore/dom/Node.h

    r261314 r261398  
    376376    bool isDescendantOrShadowDescendantOf(const Node*) const;
    377377    WEBCORE_EXPORT bool contains(const Node*) const;
    378     bool containsIncludingShadowDOM(const Node*) const;
     378    WEBCORE_EXPORT bool containsIncludingShadowDOM(const Node*) const;
    379379
    380380    // Whether or not a selection can be started in this object
  • trunk/Source/WebCore/editing/VisibleSelection.h

    r260725 r261398  
    9898    WEBCORE_EXPORT Element* rootEditableElement() const;
    9999    WEBCORE_EXPORT bool isContentEditable() const;
    100     bool hasEditableStyle() const;
     100    WEBCORE_EXPORT bool hasEditableStyle() const;
    101101    WEBCORE_EXPORT bool isContentRichlyEditable() const;
    102102    // Returns a shadow tree node for legacy shadow trees, a child of the
  • trunk/Source/WebCore/page/ChromeClient.h

    r261333 r261398  
    529529#endif
    530530
     531    virtual void animationDidFinishForElement(const Element&) { }
     532
    531533protected:
    532534    virtual ~ChromeClient() = default;
  • trunk/Source/WebKit/ChangeLog

    r261396 r261398  
     12020-05-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] caret appears in the middle of a search field when field is focused on agoda.com
     4        https://bugs.webkit.org/show_bug.cgi?id=211591
     5        <rdar://problem/60605873>
     6
     7        Reviewed by Antoine Quint.
     8
     9        The main search field on the mobile version of this website begins offscreen, with a CSS transform that moves it
     10        to the far right; tapping the button element that (visually) has a search-field-like appearance on the front
     11        page programmatically focuses the real offscreen search field, and animates it onscreen by changing the CSS
     12        transform attribute to remove the x-axis translation.
     13
     14        On iOS, the caret rect is computed and sent to the UI process via editor state updates; however, the editor
     15        state is computed immediately after focusing the input field. As such, the caret rect at this moment is computed
     16        in the middle of the animation, leaving it stuck in an unpredictable location.
     17
     18        To fix this, add plumbing to call into the WebKit client layer when an animation has ended. On iOS, if the
     19        selection is visible (i.e. a ranged selection, or editable caret), then check to see whether the element that
     20        has finished animating contains either endpoint of the selection. If so, then schedule a followup editor state
     21        update to push updated selection information to the UI process.
     22
     23        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
     24        (WebKit::WebChromeClient::animationDidFinishForElement):
     25        * WebProcess/WebCoreSupport/WebChromeClient.h:
     26
     27        Add a new client hook for when animations end.
     28
     29        * WebProcess/WebPage/WebPage.cpp:
     30        (WebKit::WebPage::animationDidFinishForElement):
     31        * WebProcess/WebPage/WebPage.h:
     32        * WebProcess/WebPage/ios/WebPageIOS.mm:
     33
     34        Add logic to schedule a new editor state update if needed, after an animation ends that might affect either
     35        the start or end of the selection.
     36
     37        (WebKit::WebPage::animationDidFinishForElement):
     38
    1392020-05-08  David Kilzer  <ddkilzer@apple.com>
    240
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp

    r261333 r261398  
    13961396#endif
    13971397
     1398void WebChromeClient::animationDidFinishForElement(const Element& element)
     1399{
     1400    m_page.animationDidFinishForElement(element);
     1401}
     1402
    13981403} // namespace WebKit
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h

    r261333 r261398  
    231231#endif
    232232
     233    void animationDidFinishForElement(const WebCore::Element&) final;
     234
    233235    RefPtr<WebCore::DisplayRefreshMonitor> createDisplayRefreshMonitor(WebCore::PlatformDisplayID) const final;
    234236
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r261364 r261398  
    70957095#endif // ENABLE(MEDIA_USAGE)
    70967096
     7097#if !PLATFORM(IOS_FAMILY)
     7098
     7099void WebPage::animationDidFinishForElement(const WebCore::Element&)
     7100{
     7101}
     7102
     7103#endif
     7104
    70977105} // namespace WebKit
    70987106
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r261364 r261398  
    405405    void didRemoveMenuItemElement(WebCore::HTMLMenuItemElement&);
    406406
     407    void animationDidFinishForElement(const WebCore::Element&);
     408
    407409    const String& overrideContentSecurityPolicy() const { return m_overrideContentSecurityPolicy; }
    408410
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r261269 r261398  
    43924392#endif
    43934393
     4394void WebPage::animationDidFinishForElement(const WebCore::Element& animatedElement)
     4395{
     4396    auto frame = makeRef(m_page->focusController().focusedOrMainFrame());
     4397    auto& selection = frame->selection().selection();
     4398    if (selection.isNoneOrOrphaned())
     4399        return;
     4400
     4401    if (selection.isCaret() && !selection.hasEditableStyle())
     4402        return;
     4403
     4404    auto scheduleEditorStateUpdateForStartOrEndContainerNodeIfNeeded = [&](const Node* container) {
     4405        if (!animatedElement.containsIncludingShadowDOM(container))
     4406            return false;
     4407
     4408        frame->selection().setCaretRectNeedsUpdate();
     4409        scheduleFullEditorStateUpdate();
     4410        return true;
     4411    };
     4412
     4413    auto startContainer = makeRefPtr(selection.start().containerNode());
     4414    if (scheduleEditorStateUpdateForStartOrEndContainerNodeIfNeeded(startContainer.get()))
     4415        return;
     4416
     4417    auto endContainer = makeRefPtr(selection.end().containerNode());
     4418    if (startContainer != endContainer)
     4419        scheduleEditorStateUpdateForStartOrEndContainerNodeIfNeeded(endContainer.get());
     4420}
     4421
    43944422} // namespace WebKit
    43954423
Note: See TracChangeset for help on using the changeset viewer.