⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 266887 in webkit


Ignore:
Timestamp:
Sep 10, 2020, 12:56:07 PM (6 years ago)
Author:
Wenson Hsieh
Message:

REGRESSION (r257839): clickpay.com - password placeholder text cannot be replaced
https://bugs.webkit.org/show_bug.cgi?id=216257
<rdar://problem/68150686>

Reviewed by Antti Koivisto.

Source/WebCore:

On clickpay.com, the field in the login form that contains the text "Password" is actually a plain text input,
referred to by the site's script as the "null text input". The page adds a focus event listener to this null
text input, and inside of this focus event listener, it reveals a hidden password field by removing an inline
display: none; style rule on the real password input element, programmatically focuses it, and then hides the
null text input by setting it to display: none; via inline style.

However, after the changes in r257839, we no longer attempt to do a style update upon programmatic focus in the
case where the programmatically focused element does not have a renderer yet (this applies to the password field
in this scenario, because it previously had display: none;). When we determine whether the newly displayed
password field is focusable using Element::isVisibleWithoutResolvingFullStyle, we then attempt to use either
the existing computed RenderStyle on the element, or perform a partial computed style resolution using the
ResolveComputedStyleMode::RenderedOnly flag.

But in the case where ElementRareData's computed style exists, it is not guaranteed to be up to date if the
inline style changed since the computed style was last set. In the context of this bug, it's actually Safari's
AutoFill logic (embedded in the injected bundle) that ends up asking for the computed style of the password
input, forcing it to be created and set (though, as demonstrated in the layout test, simply grabbing the
computed style is sufficient to replicate the bug outside of Safari).

The end result is that we'll use this stale computed style, which still believes that the password input is not
displayed, and we end up not focusing the element due to believing that the password input is hidden. To fix
this, we would need to either check whether the element has an invalid style (i.e. needsStyleRecalc()) before
attempting to use the existing computed style, or clear out the ElementRareData computed style anytime the
element's style is invalidated. However, both of these approaches will cause us to perform partial style
resolution much more aggressively, leading to a 2-3% regression in Speedometer.

To address the bug without hampering our performance wins from r257839, we add a new node flag so that we can
remember when computed styles are no longer valid due to style invalidation, and consult this flag in
Element::isVisibleWithoutResolvingFullStyle to avoid using the existing computed style.

Test: fast/forms/programmatic-focus-after-display.html

  • dom/Element.cpp:

(WebCore::Element::invalidateStyle):
(WebCore::Element::resolveComputedStyle):
(WebCore::Element::isVisibleWithoutResolvingFullStyle const):

  • dom/Node.h:

(WebCore::Node::setHasValidStyle):

LayoutTests:

Add a new layout test to verify that the bug does not occur. See WebCore/ChangeLog for more details.

  • fast/forms/programmatic-focus-after-display-expected.txt: Added.
  • fast/forms/programmatic-focus-after-display.html: Added.
Location:
trunk
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r266886 r266887  
     12020-09-10  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        REGRESSION (r257839): clickpay.com - password placeholder text cannot be replaced
     4        https://bugs.webkit.org/show_bug.cgi?id=216257
     5        <rdar://problem/68150686>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Add a new layout test to verify that the bug does not occur. See WebCore/ChangeLog for more details.
     10
     11        * fast/forms/programmatic-focus-after-display-expected.txt: Added.
     12        * fast/forms/programmatic-focus-after-display.html: Added.
     13
    1142020-09-10  Hector Lopez  <hector_i_lopez@apple.com>
    215
  • trunk/Source/WebCore/ChangeLog

    r266885 r266887  
     12020-09-10  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        REGRESSION (r257839): clickpay.com - password placeholder text cannot be replaced
     4        https://bugs.webkit.org/show_bug.cgi?id=216257
     5        <rdar://problem/68150686>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        On clickpay.com, the field in the login form that contains the text "Password" is actually a plain text input,
     10        referred to by the site's script as the "null text input". The page adds a focus event listener to this null
     11        text input, and inside of this focus event listener, it reveals a hidden password field by removing an inline
     12        `display: none;` style rule on the real password input element, programmatically focuses it, and then hides the
     13        null text input by setting it to `display: none;` via inline style.
     14
     15        However, after the changes in r257839, we no longer attempt to do a style update upon programmatic focus in the
     16        case where the programmatically focused element does not have a renderer yet (this applies to the password field
     17        in this scenario, because it previously had `display: none;`). When we determine whether the newly displayed
     18        password field is focusable using `Element::isVisibleWithoutResolvingFullStyle`, we then attempt to use either
     19        the existing computed `RenderStyle` on the element, or perform a partial computed style resolution using the
     20        `ResolveComputedStyleMode::RenderedOnly` flag.
     21
     22        But in the case where `ElementRareData`'s computed style exists, it is not guaranteed to be up to date if the
     23        inline style changed since the computed style was last set. In the context of this bug, it's actually Safari's
     24        AutoFill logic (embedded in the injected bundle) that ends up asking for the computed style of the password
     25        input, forcing it to be created and set (though, as demonstrated in the layout test, simply grabbing the
     26        computed style is sufficient to replicate the bug outside of Safari).
     27
     28        The end result is that we'll use this stale computed style, which still believes that the password input is not
     29        displayed, and we end up not focusing the element due to believing that the password input is hidden. To fix
     30        this, we would need to either check whether the element has an invalid style (i.e. `needsStyleRecalc()`) before
     31        attempting to use the existing computed style, or clear out the `ElementRareData` computed style anytime the
     32        element's style is invalidated. However, both of these approaches will cause us to perform partial style
     33        resolution much more aggressively, leading to a 2-3% regression in Speedometer.
     34
     35        To address the bug without hampering our performance wins from r257839, we add a new node flag so that we can
     36        remember when computed styles are no longer valid due to style invalidation, and consult this flag in
     37        `Element::isVisibleWithoutResolvingFullStyle` to avoid using the existing computed style.
     38
     39        Test: fast/forms/programmatic-focus-after-display.html
     40
     41        * dom/Element.cpp:
     42        (WebCore::Element::invalidateStyle):
     43        (WebCore::Element::resolveComputedStyle):
     44        (WebCore::Element::isVisibleWithoutResolvingFullStyle const):
     45        * dom/Node.h:
     46        (WebCore::Node::setHasValidStyle):
     47
    1482020-09-10  Devin Rousso  <drousso@apple.com>
    249
  • trunk/Source/WebCore/dom/Element.cpp

    r266776 r266887  
    19701970    Node::invalidateStyle(Style::Validity::ElementInvalid);
    19711971    invalidateSiblingsIfNeeded(*this);
     1972
     1973    // FIXME: This flag should be set whenever styles are invalidated while computed styles are present,
     1974    // not just in this codepath.
     1975    if (hasRareData() && elementRareData()->computedStyle())
     1976        setNodeFlag(NodeFlag::IsComputedStyleInvalidFlag);
    19721977}
    19731978
     
    33643369{
    33653370    ASSERT(isConnected());
    3366     ASSERT(!existingComputedStyle());
     3371    ASSERT(!existingComputedStyle() || hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag));
    33673372
    33683373    Deque<RefPtr<Element>, 32> elementsRequiringComputedStyle({ this });
     
    33843389        ElementRareData& rareData = element->ensureElementRareData();
    33853390        rareData.setComputedStyle(WTFMove(style));
     3391        element->clearNodeFlag(NodeFlag::IsComputedStyleInvalidFlag);
    33863392
    33873393        if (mode == ResolveComputedStyleMode::RenderedOnly && computedStyle->display() == DisplayType::None)
     
    34133419
    34143420    // Compute style in yet unstyled subtree.
    3415     auto* style = existingComputedStyle();
     3421    auto* style = hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag) ? nullptr : existingComputedStyle();
    34163422    if (!style)
    34173423        style = const_cast<Element&>(*this).resolveComputedStyle(ResolveComputedStyleMode::RenderedOnly);
  • trunk/Source/WebCore/dom/Node.h

    r266776 r266887  
    553553        ContainsFullScreenElement = 1 << 26,
    554554#endif
    555 
    556         // Bits 27-31 are free.
     555        IsComputedStyleInvalidFlag = 1 << 27,
     556
     557        // Bits 28-31 are free.
    557558    };
    558559
     
    874875    bitfields.clearFlag(NodeStyleFlag::StyleResolutionShouldRecompositeLayer);
    875876    setStyleBitfields(bitfields);
     877    clearNodeFlag(NodeFlag::IsComputedStyleInvalidFlag);
    876878}
    877879
Note: See TracChangeset for help on using the changeset viewer.