Skip to content

Commit

Permalink
Delay creation of InputType and user agent shadow trees during cloning
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=269900

Reviewed by Yusuke Suzuki.

This PR delays the construction of InputType and user agent shadow tree when cloning an input element
such that InputType is initialized after all the attributes are cloned, and its shadow tree is
constructed as the element becomes connected to a document.

* Source/WebCore/dom/Element.cpp:
(WebCore::Element::parserSetAttributes):
(WebCore::Element::cloneAttributesFromElement): Call initializeInputTypeAfterParsingOrCloning.
(WebCore::Element::cloneDataFromElement): Added an assertion to make sure the UA shadow tree's
construction has not happened yet.
* Source/WebCore/html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::HTMLInputElement):
(WebCore::HTMLInputElement::create):
(WebCore::HTMLInputElement::cloneElementWithoutAttributesAndChildren): Added.
(WebCore::HTMLInputElement::initializeInputTypeAfterParsingOrCloning): Renamed from
parserInitializeInputType.
(WebCore::HTMLInputElement::attributeChanged):
(WebCore::HTMLInputElement::copyNonAttributePropertiesFromElement): Don't update the shadow tree unless
we have already created it.
* Source/WebCore/html/HTMLInputElement.h:
* Source/WebCore/html/ValidatedFormListedElement.h:

Canonical link: https://commits.webkit.org/275200@main
  • Loading branch information
rniwa committed Feb 22, 2024
1 parent 4bd6185 commit f6dbc5c
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 18 deletions.
15 changes: 14 additions & 1 deletion Source/WebCore/dom/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2553,7 +2553,7 @@ void Element::parserSetAttributes(std::span<const Attribute> attributes)

if (auto* inputElement = dynamicDowncast<HTMLInputElement>(*this)) {
DelayedUpdateValidityScope delayedUpdateValidityScope(*inputElement);
inputElement->parserInitializeInputType();
inputElement->initializeInputTypeAfterParsingOrCloning();
}

// Use attributes instead of m_elementData because attributeChanged might modify m_elementData.
Expand Down Expand Up @@ -5127,6 +5127,10 @@ void Element::cloneAttributesFromElement(const Element& other)
other.synchronizeAllAttributes();
if (!other.m_elementData) {
m_elementData = nullptr;
if (auto* inputElement = dynamicDowncast<HTMLInputElement>(*this)) {
DelayedUpdateValidityScope delayedUpdateValidityScope(*inputElement);
inputElement->initializeInputTypeAfterParsingOrCloning();
}
return;
}

Expand Down Expand Up @@ -5159,6 +5163,11 @@ void Element::cloneAttributesFromElement(const Element& other)
else
m_elementData = other.m_elementData->makeUniqueCopy();

if (auto* inputElement = dynamicDowncast<HTMLInputElement>(*this)) {
DelayedUpdateValidityScope delayedUpdateValidityScope(*inputElement);
inputElement->initializeInputTypeAfterParsingOrCloning();
}

for (const Attribute& attribute : attributesIterator())
notifyAttributeChanged(attribute.name(), nullAtom(), attribute.value(), AttributeModificationReason::ByCloning);

Expand All @@ -5169,6 +5178,10 @@ void Element::cloneDataFromElement(const Element& other)
{
cloneAttributesFromElement(other);
copyNonAttributePropertiesFromElement(other);
#if ASSERT_ENABLED
if (auto* input = dynamicDowncast<HTMLInputElement>(*this))
ASSERT(!input->userAgentShadowRoot());
#endif
}

void Element::createUniqueElementData()
Expand Down
27 changes: 16 additions & 11 deletions Source/WebCore/html/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,24 @@ class ListAttributeTargetObserver final : public IdTargetObserver {

static constexpr int maxSavedResults = 256;

HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form, CreationType creationType)
: HTMLTextFormControlElement(tagName, document, form)
, m_parsingInProgress(createdByParser)
, m_parsingInProgress(creationType == CreationType::ByParser)
// m_inputType is lazily created when constructed by the parser to avoid constructing unnecessarily a text inputType,
// just to destroy them when the |type| attribute gets set by the parser to something else than 'text'.
, m_inputType(createdByParser ? nullptr : RefPtr { TextInputType::create(*this) })
, m_inputType(creationType != CreationType::Normal ? nullptr : RefPtr { TextInputType::create(*this) })
{
ASSERT(hasTagName(inputTag));
}

Ref<HTMLInputElement> HTMLInputElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
{
return adoptRef(*new HTMLInputElement(tagName, document, form, createdByParser));
return adoptRef(*new HTMLInputElement(tagName, document, form, createdByParser ? CreationType::ByParser : CreationType::Normal));
}

Ref<Element> HTMLInputElement::cloneElementWithoutAttributesAndChildren(Document& targetDocument)
{
return adoptRef(*new HTMLInputElement(tagQName(), targetDocument, nullptr, CreationType::ByCloning));
}

HTMLImageLoader& HTMLInputElement::ensureImageLoader()
Expand Down Expand Up @@ -753,9 +758,8 @@ void HTMLInputElement::collectPresentationalHintsForAttribute(const QualifiedNam
}
}

void HTMLInputElement::parserInitializeInputType()
void HTMLInputElement::initializeInputTypeAfterParsingOrCloning()
{
ASSERT(m_parsingInProgress);
ASSERT(!m_inputType);

auto& type = attributeWithoutSynchronization(typeAttr);
Expand Down Expand Up @@ -785,13 +789,13 @@ void HTMLInputElement::attributeChanged(const QualifiedName& name, const AtomStr

switch (name.nodeName()) {
case AttributeNames::typeAttr:
if (attributeModificationReason == AttributeModificationReason::Parser)
return; // parserSetAttributes have taken care of this
if (attributeModificationReason != AttributeModificationReason::Directly)
return; // initializeInputTypeAfterParsingOrCloning has taken care of this.
updateType(newValue);
break;
case AttributeNames::valueAttr:
if (attributeModificationReason == AttributeModificationReason::Parser)
return; // parserSetAttributes have taken care of this
if (attributeModificationReason != AttributeModificationReason::Directly)
return; // initializeInputTypeAfterParsingOrCloning has taken care of this.
// Changes to the value attribute may change whether or not this element has a default value.
// If this field is autocomplete=off that might affect the return value of needsSuspensionCallback.
if (m_autocomplete == Off) {
Expand Down Expand Up @@ -1129,7 +1133,8 @@ void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& sour

updateValidity();
setFormControlValueMatchesRenderer(false);
m_inputType->updateInnerTextValue();
if (m_inputType->hasCreatedShadowSubtree())
m_inputType->updateInnerTextValue();
}

String HTMLInputElement::value() const
Expand Down
12 changes: 7 additions & 5 deletions Source/WebCore/html/HTMLInputElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct InputElementClickState {

enum class WasSetByJavaScript : bool { No, Yes };

class HTMLInputElement : public HTMLTextFormControlElement {
class HTMLInputElement final : public HTMLTextFormControlElement {
WTF_MAKE_ISO_ALLOCATED(HTMLInputElement);
public:
static Ref<HTMLInputElement> create(const QualifiedName&, Document&, HTMLFormElement*, bool createdByParser);
Expand Down Expand Up @@ -345,14 +345,16 @@ class HTMLInputElement : public HTMLTextFormControlElement {
bool isSwitchVisuallyOn() const;
float switchAnimationPressedProgress() const;

void parserInitializeInputType();
void initializeInputTypeAfterParsingOrCloning();

protected:
HTMLInputElement(const QualifiedName&, Document&, HTMLFormElement*, bool createdByParser);
private:
enum class CreationType : uint8_t { Normal, ByParser, ByCloning };
HTMLInputElement(const QualifiedName&, Document&, HTMLFormElement*, CreationType);

void defaultEventHandler(Event&) final;

private:
Ref<Element> cloneElementWithoutAttributesAndChildren(Document&) override;

enum AutoCompleteSetting : uint8_t { Uninitialized, On, Off };
static constexpr int defaultSize = 20;

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/html/ValidatedFormListedElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ValidatedFormListedElement : public FormListedElement {
WEBCORE_EXPORT bool isFocusingWithValidationMessage() const;
// This must be called when a validation constraint or control value is changed.
void updateValidity();
void setCustomValidity(const String&) override;
WEBCORE_EXPORT void setCustomValidity(const String&) override;

void setDisabledByAncestorFieldset(bool isDisabled);
virtual void reset() { }
Expand Down

0 comments on commit f6dbc5c

Please sign in to comment.