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

Changeset 177664 in webkit


Ignore:
Timestamp:
Dec 22, 2014, 4:38:52 PM (12 years ago)
Author:
benjamin@webkit.org
Message:

Form elements should match :valid and :invalid based on their associated elements
https://bugs.webkit.org/show_bug.cgi?id=139850

Patch by Benjamin Poulain <bpoulain@apple.com> on 2014-12-22
Reviewed by Darin Adler.

Source/WebCore:

In the latest HTML spec, form elements can match :valid or :invalid based
on their associated element.

The tricky part is that object lifetime is a mess. When elements are associated
with forms by the parser, the form is set by the constructor of HTMLFormControlElement.
At that point, the real object has not been initialized yet which
makes it impossible to find its validity.

To work around the lifetime problem, the code of HTMLFormControlElement::didChangeForm()
uses m_willValidateInitialized and m_willValidate direclty instead
of invoking willValidate(). That way we don't try to validate an incomplete object.

When the object really validates, HTMLFormControlElement::setNeedsWillValidateCheck()
takes care of updating the form.

Tests: fast/css/pseudo-invalid-form-and-fieldset-basics.html

fast/css/pseudo-invalid-form-basics.html
fast/css/pseudo-invalid-form-dynamically-created-basics.html
fast/css/pseudo-invalid-form-invalidation-optimization.html
fast/css/pseudo-valid-form-and-fieldset-basics.html
fast/css/pseudo-valid-form-basics.html
fast/css/pseudo-valid-form-dynamically-created-basics.html
fast/css/pseudo-valid-form-invalidation-optimization.html
fast/selectors/invalid-form-style-update-1.html
fast/selectors/invalid-form-style-update-2.html
fast/selectors/invalid-form-style-update-3.html
fast/selectors/valid-form-style-update-1.html
fast/selectors/valid-form-style-update-2.html
fast/selectors/valid-form-style-update-3.html

  • css/StyleResolver.cpp:

(WebCore::StyleResolver::canShareStyleWithControl):
(WebCore::StyleResolver::canShareStyleWithElement):
HTMLFormElement is not a FormControl, we have to generalize the test
for :valid/:invalid with style sharing.

  • html/HTMLFormControlElement.cpp:

(WebCore::HTMLFormControlElement::~HTMLFormControlElement):
Since we now have willChangeForm(), didChangeForm(), we have to null
the form ourself, as documented by FormAssociatedElement.

(WebCore::HTMLFormControlElement::setNeedsWillValidateCheck):
(WebCore::HTMLFormControlElement::willChangeForm):
(WebCore::HTMLFormControlElement::didChangeForm):
(WebCore::HTMLFormControlElement::updateValidity):
Update the owner form when any of the associated form element changes
to invalid.

  • html/HTMLFormControlElement.h:
  • html/HTMLFormElement.cpp:

(WebCore::HTMLFormElement::registerInvalidAssociatedFormControl):
(WebCore::HTMLFormElement::removeInvalidAssociatedFormControlIfNeeded):
(WebCore::HTMLFormElement::matchesValidPseudoClass):
(WebCore::HTMLFormElement::matchesInvalidPseudoClass):
This is very similar to the code of fieldset, but we have much weaker
invariants due to the insane way FormAssociatedElement initializes.

  • html/HTMLFormElement.h:
  • html/HTMLInputElement.cpp:

(WebCore::HTMLInputElement::initializeInputType):
For completeness, we should always validate when changing the type.
The code was only doing that when the element was inserted into the tree,
that was too fragile.

LayoutTests:

The style update tests are largely based on the fieldset test suite.
The other tests are for various aspects of the patch.

  • fast/css/pseudo-invalid-form-and-fieldset-basics-expected.html: Added.
  • fast/css/pseudo-invalid-form-and-fieldset-basics.html: Added.
  • fast/css/pseudo-invalid-form-basics-expected.html: Added.
  • fast/css/pseudo-invalid-form-basics.html: Added.
  • fast/css/pseudo-invalid-form-dynamically-created-basics-expected.html: Added.
  • fast/css/pseudo-invalid-form-dynamically-created-basics.html: Added.
  • fast/css/pseudo-invalid-form-invalidation-optimization-expected.txt: Added.
  • fast/css/pseudo-invalid-form-invalidation-optimization.html: Added.
  • fast/css/pseudo-valid-form-and-fieldset-basics-expected.html: Added.
  • fast/css/pseudo-valid-form-and-fieldset-basics.html: Added.
  • fast/css/pseudo-valid-form-basics-expected.html: Added.
  • fast/css/pseudo-valid-form-basics.html: Added.
  • fast/css/pseudo-valid-form-dynamically-created-basics-expected.html: Added.
  • fast/css/pseudo-valid-form-dynamically-created-basics.html: Added.
  • fast/css/pseudo-valid-form-invalidation-optimization-expected.txt: Added.
  • fast/css/pseudo-valid-form-invalidation-optimization.html: Added.
  • fast/selectors/invalid-form-style-update-1-expected.txt: Added.
  • fast/selectors/invalid-form-style-update-1.html: Added.
  • fast/selectors/invalid-form-style-update-2-expected.txt: Added.
  • fast/selectors/invalid-form-style-update-2.html: Added.
  • fast/selectors/invalid-form-style-update-3-expected.txt: Added.
  • fast/selectors/invalid-form-style-update-3.html: Added.
  • fast/selectors/valid-form-style-update-1-expected.txt: Added.
  • fast/selectors/valid-form-style-update-1.html: Added.
  • fast/selectors/valid-form-style-update-2-expected.txt: Added.
  • fast/selectors/valid-form-style-update-2.html: Added.
  • fast/selectors/valid-form-style-update-3-expected.txt: Added.
  • fast/selectors/valid-form-style-update-3.html: Added.
Location:
trunk
Files:
28 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r177660 r177664  
     12014-12-22  Benjamin Poulain  <bpoulain@apple.com>
     2
     3        Form elements should match :valid and :invalid based on their associated elements
     4        https://bugs.webkit.org/show_bug.cgi?id=139850
     5
     6        Reviewed by Darin Adler.
     7
     8        The style update tests are largely based on the fieldset test suite.
     9        The other tests are for various aspects of the patch.
     10
     11        * fast/css/pseudo-invalid-form-and-fieldset-basics-expected.html: Added.
     12        * fast/css/pseudo-invalid-form-and-fieldset-basics.html: Added.
     13        * fast/css/pseudo-invalid-form-basics-expected.html: Added.
     14        * fast/css/pseudo-invalid-form-basics.html: Added.
     15        * fast/css/pseudo-invalid-form-dynamically-created-basics-expected.html: Added.
     16        * fast/css/pseudo-invalid-form-dynamically-created-basics.html: Added.
     17        * fast/css/pseudo-invalid-form-invalidation-optimization-expected.txt: Added.
     18        * fast/css/pseudo-invalid-form-invalidation-optimization.html: Added.
     19        * fast/css/pseudo-valid-form-and-fieldset-basics-expected.html: Added.
     20        * fast/css/pseudo-valid-form-and-fieldset-basics.html: Added.
     21        * fast/css/pseudo-valid-form-basics-expected.html: Added.
     22        * fast/css/pseudo-valid-form-basics.html: Added.
     23        * fast/css/pseudo-valid-form-dynamically-created-basics-expected.html: Added.
     24        * fast/css/pseudo-valid-form-dynamically-created-basics.html: Added.
     25        * fast/css/pseudo-valid-form-invalidation-optimization-expected.txt: Added.
     26        * fast/css/pseudo-valid-form-invalidation-optimization.html: Added.
     27        * fast/selectors/invalid-form-style-update-1-expected.txt: Added.
     28        * fast/selectors/invalid-form-style-update-1.html: Added.
     29        * fast/selectors/invalid-form-style-update-2-expected.txt: Added.
     30        * fast/selectors/invalid-form-style-update-2.html: Added.
     31        * fast/selectors/invalid-form-style-update-3-expected.txt: Added.
     32        * fast/selectors/invalid-form-style-update-3.html: Added.
     33        * fast/selectors/valid-form-style-update-1-expected.txt: Added.
     34        * fast/selectors/valid-form-style-update-1.html: Added.
     35        * fast/selectors/valid-form-style-update-2-expected.txt: Added.
     36        * fast/selectors/valid-form-style-update-2.html: Added.
     37        * fast/selectors/valid-form-style-update-3-expected.txt: Added.
     38        * fast/selectors/valid-form-style-update-3.html: Added.
     39
    1402014-12-22  Alexey Proskuryakov  <ap@apple.com>
    241
  • trunk/Source/WebCore/ChangeLog

    r177663 r177664  
     12014-12-22  Benjamin Poulain  <bpoulain@apple.com>
     2
     3        Form elements should match :valid and :invalid based on their associated elements
     4        https://bugs.webkit.org/show_bug.cgi?id=139850
     5
     6        Reviewed by Darin Adler.
     7
     8        In the latest HTML spec, form elements can match :valid or :invalid based
     9        on their associated element.
     10
     11        The tricky part is that object lifetime is a mess. When elements are associated
     12        with forms by the parser, the form is set by the constructor of HTMLFormControlElement.
     13        At that point, the real object has not been initialized yet which
     14        makes it impossible to find its validity.
     15
     16        To work around the lifetime problem, the code of HTMLFormControlElement::didChangeForm()
     17        uses m_willValidateInitialized and m_willValidate direclty instead
     18        of invoking willValidate(). That way we don't try to validate an incomplete object.
     19
     20        When the object really validates, HTMLFormControlElement::setNeedsWillValidateCheck()
     21        takes care of updating the form.
     22
     23        Tests: fast/css/pseudo-invalid-form-and-fieldset-basics.html
     24               fast/css/pseudo-invalid-form-basics.html
     25               fast/css/pseudo-invalid-form-dynamically-created-basics.html
     26               fast/css/pseudo-invalid-form-invalidation-optimization.html
     27               fast/css/pseudo-valid-form-and-fieldset-basics.html
     28               fast/css/pseudo-valid-form-basics.html
     29               fast/css/pseudo-valid-form-dynamically-created-basics.html
     30               fast/css/pseudo-valid-form-invalidation-optimization.html
     31               fast/selectors/invalid-form-style-update-1.html
     32               fast/selectors/invalid-form-style-update-2.html
     33               fast/selectors/invalid-form-style-update-3.html
     34               fast/selectors/valid-form-style-update-1.html
     35               fast/selectors/valid-form-style-update-2.html
     36               fast/selectors/valid-form-style-update-3.html
     37
     38        * css/StyleResolver.cpp:
     39        (WebCore::StyleResolver::canShareStyleWithControl):
     40        (WebCore::StyleResolver::canShareStyleWithElement):
     41        HTMLFormElement is not a FormControl, we have to generalize the test
     42        for :valid/:invalid with style sharing.
     43
     44        * html/HTMLFormControlElement.cpp:
     45        (WebCore::HTMLFormControlElement::~HTMLFormControlElement):
     46        Since we now have willChangeForm(), didChangeForm(), we have to null
     47        the form ourself, as documented by FormAssociatedElement.
     48
     49        (WebCore::HTMLFormControlElement::setNeedsWillValidateCheck):
     50        (WebCore::HTMLFormControlElement::willChangeForm):
     51        (WebCore::HTMLFormControlElement::didChangeForm):
     52        (WebCore::HTMLFormControlElement::updateValidity):
     53        Update the owner form when any of the associated form element changes
     54        to invalid.
     55
     56        * html/HTMLFormControlElement.h:
     57        * html/HTMLFormElement.cpp:
     58        (WebCore::HTMLFormElement::registerInvalidAssociatedFormControl):
     59        (WebCore::HTMLFormElement::removeInvalidAssociatedFormControlIfNeeded):
     60        (WebCore::HTMLFormElement::matchesValidPseudoClass):
     61        (WebCore::HTMLFormElement::matchesInvalidPseudoClass):
     62        This is very similar to the code of fieldset, but we have much weaker
     63        invariants due to the insane way FormAssociatedElement initializes.
     64
     65        * html/HTMLFormElement.h:
     66        * html/HTMLInputElement.cpp:
     67        (WebCore::HTMLInputElement::initializeInputType):
     68        For completeness, we should always validate when changing the type.
     69        The code was only doing that when the element was inserted into the tree,
     70        that was too fragile.
     71
    1722014-12-22  Anders Carlsson  <andersca@apple.com>
    273
  • trunk/Source/WebCore/css/StyleResolver.cpp

    r177616 r177664  
    521521        return false;
    522522
    523     if (element->matchesValidPseudoClass() != state.element()->matchesValidPseudoClass())
    524         return false;
    525 
    526     if (element->matchesInvalidPseudoClass() != state.element()->matchesValidPseudoClass())
    527         return false;
    528 
    529523    if (element->isInRange() != state.element()->isInRange())
    530524        return false;
     
    652646        }
    653647    }
     648
     649    if (element->matchesValidPseudoClass() != state.element()->matchesValidPseudoClass())
     650        return false;
     651
     652    if (element->matchesInvalidPseudoClass() != state.element()->matchesValidPseudoClass())
     653        return false;
    654654
    655655#if ENABLE(VIDEO_TRACK)
  • trunk/Source/WebCore/html/HTMLFormControlElement.cpp

    r177538 r177664  
    6868HTMLFormControlElement::~HTMLFormControlElement()
    6969{
     70    // The calls willChangeForm() and didChangeForm() are virtual, we want the
     71    // form to be reset while this object still exists.
     72    setForm(nullptr);
    7073}
    7174
     
    418421    setNeedsStyleRecalc();
    419422
    420     if (!m_willValidate && !wasValid)
     423    if (!m_willValidate && !wasValid) {
    421424        removeInvalidElementToAncestorFromInsertionPoint(*this, parentNode());
     425        if (HTMLFormElement* form = this->form())
     426            form->removeInvalidAssociatedFormControlIfNeeded(*this);
     427    }
    422428
    423429    if (!m_willValidate)
     
    465471}
    466472
     473void HTMLFormControlElement::willChangeForm()
     474{
     475    if (HTMLFormElement* form = this->form())
     476        form->removeInvalidAssociatedFormControlIfNeeded(*this);
     477    FormAssociatedElement::willChangeForm();
     478}
     479
     480void HTMLFormControlElement::didChangeForm()
     481{
     482    FormAssociatedElement::didChangeForm();
     483    if (HTMLFormElement* form = this->form()) {
     484        if (m_willValidateInitialized && m_willValidate && !isValidFormControlElement())
     485            form->registerInvalidAssociatedFormControl(*this);
     486    }
     487}
     488
    467489void HTMLFormControlElement::updateValidity()
    468490{
     
    476498        setNeedsStyleRecalc();
    477499
    478         if (!m_isValid)
     500        if (!m_isValid) {
    479501            addInvalidElementToAncestorFromInsertionPoint(*this, parentNode());
    480         else
     502            if (HTMLFormElement* form = this->form())
     503                form->registerInvalidAssociatedFormControl(*this);
     504        } else {
    481505            removeInvalidElementToAncestorFromInsertionPoint(*this, parentNode());
     506            if (HTMLFormElement* form = this->form())
     507                form->removeInvalidAssociatedFormControlIfNeeded(*this);
     508        }
    482509    }
    483510
  • trunk/Source/WebCore/html/HTMLFormControlElement.h

    r177293 r177664  
    147147    bool validationMessageShadowTreeContains(const Node&) const;
    148148
     149    virtual void willChangeForm() override;
     150    virtual void didChangeForm() override;
     151
    149152private:
    150153    virtual void refFormAssociatedElement() override { ref(); }
  • trunk/Source/WebCore/html/HTMLFormElement.cpp

    r176964 r177664  
    623623}
    624624
     625void HTMLFormElement::registerInvalidAssociatedFormControl(const HTMLFormControlElement& formControlElement)
     626{
     627    ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(formControlElement), "FieldSet are never candidates for constraint validation.");
     628    ASSERT(static_cast<const Element&>(formControlElement).matchesInvalidPseudoClass());
     629
     630    if (m_invalidAssociatedFormControls.isEmpty())
     631        setNeedsStyleRecalc();
     632    m_invalidAssociatedFormControls.add(&formControlElement);
     633}
     634
     635void HTMLFormElement::removeInvalidAssociatedFormControlIfNeeded(const HTMLFormControlElement& formControlElement)
     636{
     637    if (m_invalidAssociatedFormControls.remove(&formControlElement)) {
     638        if (m_invalidAssociatedFormControls.isEmpty())
     639            setNeedsStyleRecalc();
     640    }
     641}
     642
    625643bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
    626644{
     
    788806}
    789807
     808bool HTMLFormElement::matchesValidPseudoClass() const
     809{
     810    return m_invalidAssociatedFormControls.isEmpty();
     811}
     812
     813bool HTMLFormElement::matchesInvalidPseudoClass() const
     814{
     815    return !m_invalidAssociatedFormControls.isEmpty();
     816}
     817
    790818bool HTMLFormElement::hasNamedElement(const AtomicString& name)
    791819{
  • trunk/Source/WebCore/html/HTMLFormElement.h

    r176502 r177664  
    7979    void removeFormElement(FormAssociatedElement*);
    8080
     81    void registerInvalidAssociatedFormControl(const HTMLFormControlElement&);
     82    void removeInvalidAssociatedFormControlIfNeeded(const HTMLFormControlElement&);
     83
    8184    void registerImgElement(HTMLImageElement*);
    8285    void removeImgElement(HTMLImageElement*);
     
    174177    void removeFromPastNamesMap(FormNamedItem*);
    175178
     179    virtual bool matchesValidPseudoClass() const override;
     180    virtual bool matchesInvalidPseudoClass() const override;
     181
    176182    typedef HashMap<RefPtr<AtomicStringImpl>, FormNamedItem*> PastNamesMap;
    177183
     
    185191    Vector<FormAssociatedElement*> m_associatedElements;
    186192    Vector<HTMLImageElement*> m_imageElements;
     193    HashSet<const HTMLFormControlElement*> m_invalidAssociatedFormControls;
    187194
    188195    bool m_wasUserSubmitted;
  • trunk/Source/WebCore/html/HTMLInputElement.cpp

    r177444 r177664  
    617617        m_inputType = InputType::createText(*this);
    618618        ensureUserAgentShadowRoot();
     619        setNeedsWillValidateCheck();
    619620        return;
    620621    }
     
    623624    m_inputType = InputType::create(*this, type);
    624625    ensureUserAgentShadowRoot();
     626    setNeedsWillValidateCheck();
    625627    registerForSuspensionCallbackIfNeeded();
    626628    runPostTypeUpdateTasks();
Note: See TracChangeset for help on using the changeset viewer.