Title: [267631] branches/safari-610-branch
Revision
267631
Author
[email protected]
Date
2020-09-26 14:08:38 -0700 (Sat, 26 Sep 2020)

Log Message

Cherry-pick r266887. rdar://problem/69586659

    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.

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@266887 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Added Paths

Diff

Modified: branches/safari-610-branch/LayoutTests/ChangeLog (267630 => 267631)


--- branches/safari-610-branch/LayoutTests/ChangeLog	2020-09-26 21:08:35 UTC (rev 267630)
+++ branches/safari-610-branch/LayoutTests/ChangeLog	2020-09-26 21:08:38 UTC (rev 267631)
@@ -1,5 +1,78 @@
 2020-09-25  Alan Coon  <[email protected]>
 
+        Cherry-pick r266887. rdar://problem/69586659
+
+    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.
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@266887 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2020-09-10  Wenson Hsieh  <[email protected]>
+
+            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.
+
+            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.
+
+2020-09-25  Alan Coon  <[email protected]>
+
         Cherry-pick r266825. rdar://problem/69583178
 
     REGRESSION (Async overflow scroll): Truncated scrollbars in facebook chat

Added: branches/safari-610-branch/LayoutTests/fast/forms/programmatic-focus-after-display-expected.txt (0 => 267631)


--- branches/safari-610-branch/LayoutTests/fast/forms/programmatic-focus-after-display-expected.txt	                        (rev 0)
+++ branches/safari-610-branch/LayoutTests/fast/forms/programmatic-focus-after-display-expected.txt	2020-09-26 21:08:38 UTC (rev 267631)
@@ -0,0 +1,11 @@
+This test verifies that programmatically focusing a newly displayed input field changes the active element. To test manually, tap or click the input field below.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.activeElement is passwordInput
+PASS didFinishActivatingElement became true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: branches/safari-610-branch/LayoutTests/fast/forms/programmatic-focus-after-display.html (0 => 267631)


--- branches/safari-610-branch/LayoutTests/fast/forms/programmatic-focus-after-display.html	                        (rev 0)
+++ branches/safari-610-branch/LayoutTests/fast/forms/programmatic-focus-after-display.html	2020-09-26 21:08:38 UTC (rev 267631)
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+    <script src=""
+    <script src=""
+    <style>
+        input {
+            font-size: 20px;
+        }
+
+        input[type="password"] {
+            display: none;
+        }
+    </style>
+</head>
+<body>
+    <input type="text" value="Click me">
+    <input type="password">
+    <script>
+        jsTestIsAsync = true;
+        didFinishActivatingElement = false;
+        textInput = document.querySelector("input[type='text']");
+        passwordInput = document.querySelector("input[type='password']");
+
+        textInput.addEventListener("focus", async () => {
+            getComputedStyle(passwordInput).display; // This is necessary in order to reproduce the bug.
+            passwordInput.style.display = "block";
+            passwordInput.focus();
+
+            shouldBe("document.activeElement", "passwordInput");
+            passwordInput.remove();
+            textInput.remove();
+
+            await new Promise(resolve => shouldBecomeEqual("didFinishActivatingElement", "true", resolve));
+            finishJSTest();
+        });
+
+        addEventListener("load", async () => {
+            description("This test verifies that programmatically focusing a newly displayed input field changes the active element. To test manually, tap or click the input field below.");
+            if (window.testRunner)
+                await UIHelper.activateElement(textInput);
+            didFinishActivatingElement = true;
+        });
+    </script>
+</body>
+</html>

Modified: branches/safari-610-branch/Source/WebCore/ChangeLog (267630 => 267631)


--- branches/safari-610-branch/Source/WebCore/ChangeLog	2020-09-26 21:08:35 UTC (rev 267630)
+++ branches/safari-610-branch/Source/WebCore/ChangeLog	2020-09-26 21:08:38 UTC (rev 267631)
@@ -1,5 +1,112 @@
 2020-09-25  Alan Coon  <[email protected]>
 
+        Cherry-pick r266887. rdar://problem/69586659
+
+    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.
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@266887 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2020-09-10  Wenson Hsieh  <[email protected]>
+
+            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.
+
+            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):
+
+2020-09-25  Alan Coon  <[email protected]>
+
         Cherry-pick r266884. rdar://problem/69583178
 
     Revert accidental hard-coding of compositing logging from r266825.

Modified: branches/safari-610-branch/Source/WebCore/dom/Element.cpp (267630 => 267631)


--- branches/safari-610-branch/Source/WebCore/dom/Element.cpp	2020-09-26 21:08:35 UTC (rev 267630)
+++ branches/safari-610-branch/Source/WebCore/dom/Element.cpp	2020-09-26 21:08:38 UTC (rev 267631)
@@ -1944,6 +1944,11 @@
 {
     Node::invalidateStyle(Style::Validity::ElementInvalid);
     invalidateSiblingsIfNeeded(*this);
+
+    // FIXME: This flag should be set whenever styles are invalidated while computed styles are present,
+    // not just in this codepath.
+    if (hasRareData() && elementRareData()->computedStyle())
+        setNodeFlag(NodeFlag::IsComputedStyleInvalidFlag);
 }
 
 void Element::invalidateStyleAndLayerComposition()
@@ -3317,7 +3322,7 @@
 const RenderStyle* Element::resolveComputedStyle(ResolveComputedStyleMode mode)
 {
     ASSERT(isConnected());
-    ASSERT(!existingComputedStyle());
+    ASSERT(!existingComputedStyle() || hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag));
 
     Deque<RefPtr<Element>, 32> elementsRequiringComputedStyle({ this });
     const RenderStyle* computedStyle = nullptr;
@@ -3337,6 +3342,7 @@
         computedStyle = style.get();
         ElementRareData& rareData = element->ensureElementRareData();
         rareData.setComputedStyle(WTFMove(style));
+        element->clearNodeFlag(NodeFlag::IsComputedStyleInvalidFlag);
 
         if (mode == ResolveComputedStyleMode::RenderedOnly && computedStyle->display() == DisplayType::None)
             return nullptr;
@@ -3366,7 +3372,7 @@
         return renderStyle() && renderStyle()->visibility() == Visibility::Visible;
 
     // Compute style in yet unstyled subtree.
-    auto* style = existingComputedStyle();
+    auto* style = hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag) ? nullptr : existingComputedStyle();
     if (!style)
         style = const_cast<Element&>(*this).resolveComputedStyle(ResolveComputedStyleMode::RenderedOnly);
 

Modified: branches/safari-610-branch/Source/WebCore/dom/Node.h (267630 => 267631)


--- branches/safari-610-branch/Source/WebCore/dom/Node.h	2020-09-26 21:08:35 UTC (rev 267630)
+++ branches/safari-610-branch/Source/WebCore/dom/Node.h	2020-09-26 21:08:38 UTC (rev 267631)
@@ -553,7 +553,7 @@
 
         ChildrenAffectedByFirstChildRulesFlag = 1 << 25,
         ChildrenAffectedByLastChildRulesFlag = 1 << 26,
-        // UnusedFlag = 1 << 27,
+        IsComputedStyleInvalidFlag = 1 << 27,
 
         AffectsNextSiblingElementStyle = 1 << 28,
         StyleIsAffectedByPreviousSibling = 1 << 29,
@@ -775,6 +775,7 @@
 {
     m_nodeFlags &= ~StyleValidityMask;
     clearFlag(StyleResolutionShouldRecompositeLayerFlag);
+    clearNodeFlag(NodeFlag::IsComputedStyleInvalidFlag);
 }
 
 inline void Node::setTreeScopeRecursively(TreeScope& newTreeScope)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to