Diff
Modified: trunk/LayoutTests/ChangeLog (290283 => 290284)
--- trunk/LayoutTests/ChangeLog 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/LayoutTests/ChangeLog 2022-02-22 03:36:52 UTC (rev 290284)
@@ -1,3 +1,16 @@
+2022-02-21 Cameron McCormack <hey...@apple.com>
+
+ Make input element UA shadow tree creation lazy
+ https://bugs.webkit.org/show_bug.cgi?id=236747
+
+ Reviewed by Aditya Keerthi.
+
+ * fast/forms/lazy-shadow-tree-creation-expected.html: Added.
+ * fast/forms/lazy-shadow-tree-creation.html: Added.
+ * fast/forms/lazy-shadow-tree-creation.js: Added.
+ (supportsType):
+ (makeAndAppendInput):
+
2022-02-21 Jon Lee <jon...@apple.com>
Unreviewed gardening for the GPU Process bots.
Added: trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation-expected.html (0 => 290284)
--- trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation-expected.html (rev 0)
+++ trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation-expected.html 2022-02-22 03:36:52 UTC (rev 290284)
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+<script src=""
+<script>
+run({ useParser: true });
+</script>
Added: trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation.html (0 => 290284)
--- trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation.html (rev 0)
+++ trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation.html 2022-02-22 03:36:52 UTC (rev 290284)
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+<script src=""
+<script>
+run({ useParser: false });
+</script>
Added: trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation.js (0 => 290284)
--- trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation.js (rev 0)
+++ trunk/LayoutTests/fast/forms/lazy-shadow-tree-creation.js 2022-02-22 03:36:52 UTC (rev 290284)
@@ -0,0 +1,57 @@
+const types = {
+ "color": { value: "#008000" },
+ "date": { value: "2022-01-01" },
+ "datetime-local": { value: "2022-01-01" },
+ "email": { value: "he...@example.com" },
+ "file": { },
+ "month": { value: "2022-01" },
+ "number": { value: "123" },
+ "password": { },
+ "range": { value: "75" },
+ "search": { value: "abc" },
+ "telephone": { value: "+61300000000" },
+ "text": { value: "abc" },
+ "time": { value: "10:00" },
+ "url": { value: "https://example.com/" },
+ "week": { value: "2022-W1" },
+};
+
+function supportsType(typeName) {
+ let e = document.createElement("input");
+ e.type = typeName;
+ return e.type == typeName;
+}
+
+function makeAndAppendInput(options, typeName, attributes = { }) {
+ let span = document.createElement("span");
+ document.body.append(span);
+
+ if (options.useParser) {
+ let tag = `<input type="${typeName}"`;
+ for (let name in attributes)
+ tag += ` ${name}="${attributes[name]}"`;
+ tag += ">";
+ span.innerHTML = tag;
+ } else {
+ let input = document.createElement("input");
+ input.type = typeName;
+ for (let name in attributes)
+ input.setAttribute(name, attributes[name]);
+ span.append(input);
+ }
+}
+
+function run(options) {
+ for (let typeName in types) {
+ if (!supportsType(typeName))
+ continue;
+
+ let type = types[typeName];
+ makeAndAppendInput(options, typeName);
+
+ if (type.value)
+ makeAndAppendInput(options, typeName, { value: type.value });
+
+ makeAndAppendInput(options, typeName, { value: type.value, disabled: "disabled" });
+ }
+}
Modified: trunk/Source/WebCore/ChangeLog (290283 => 290284)
--- trunk/Source/WebCore/ChangeLog 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/ChangeLog 2022-02-22 03:36:52 UTC (rev 290284)
@@ -1,3 +1,184 @@
+2022-02-21 Cameron McCormack <hey...@apple.com>
+
+ Make input element UA shadow tree creation lazy
+ https://bugs.webkit.org/show_bug.cgi?id=236747
+
+ Reviewed by Aditya Keerthi.
+
+ We currently delay InputType creation for parser inserted elements until
+ just after the attributes have been set, so that we don't wastefully
+ create an InputType and the UA shadow tree creation if a non-text
+ type="" was specified on the tag. We don't do anything similar for
+ script inserted input elements. We could make the InputType creation
+ lazy, but most of the wasted time is due to the shadow tree creation.
+
+ This patch makes InputType shadow tree creation lazy by delaying it
+ until one of the following happens:
+
+ 1. the element is inserted into the document
+ 2. the type="" or value="" attributes are changed before the element
+ is inserted into the document
+ 3. any DOM methods that need access to the innerTextElement() are
+ called on the element before the element is inserted into the
+ document
+
+ Not all places where we call innerTextElement() on the
+ HTMLInputElement are safe to lazily create the shadow trees, so we
+ have two accessors:
+
+ - innerTextElement() returns the inner text element if it's been
+ created already
+ - innerTextElementCreatingShadowSubtreeIfNeeded will perform the lazy
+ shadow tree construction if it hasn't already been done
+
+ Since the existing
+ createShadowSubtreeAndUpdateInnerTextElementEditability function has
+ more responsibility than just creating the subtree and ensuring the
+ editability is set appropriately, it's renamed to a more manageable
+ createShadowSubtree.
+
+ This change is a 0.5% progression on Speedometer 2.
+
+ Test: fast/forms/lazy-shadow-tree-creation.html
+
+ * html/BaseDateAndTimeInputType.h:
+ * html/BaseDateAndTimeInputType.cpp:
+ (WebCore::BaseDateAndTimeInputType::createShadowSubtree):
+ (WebCore::BaseDateAndTimeInputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ * html/ColorInputType.h:
+ * html/ColorInputType.cpp:
+ (WebCore::ColorInputType::createShadowSubtree):
+ (WebCore::ColorInputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ * html/FileInputType.h:
+ * html/FileInputType.cpp:
+ (WebCore::FileInputType::createShadowSubtree):
+ (WebCore::FileInputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ * html/InputType.cpp:
+ (WebCore::InputType::createShadowSubtree):
+ (WebCore::InputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ * html/RangeInputType.h:
+ * html/RangeInputType.cpp:
+ (WebCore::RangeInputType::createShadowSubtree):
+ (WebCore::RangeInputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ * html/SearchInputType.h:
+ * html/SearchInputType.cpp:
+ (WebCore::SearchInputType::createShadowSubtree):
+ (WebCore::SearchInputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ Renamed createShadowSubtreeAndUpdateInnerTextElementEditability to
+ createShadowSubtree and remove the "isInnerTextElementEditable"
+ argument, since we can ask the element() for its value if needed.
+ createShadowSubtree is now also responsible for creating the shadow
+ root.
+
+ * html/TextFieldInputType.h:
+ * html/TextFieldInputType.cpp:
+ (WebCore::TextFieldInputType::createShadowSubtree):
+ (WebCore::TextFieldInputType::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ Renamed. Ensure all shadow tree state is up to date now that it can be
+ created later.
+
+ * html/InputType.h:
+ * html/InputType.cpp:
+ (WebCore::InputType::createShadowSubtree):
+ (WebCore::InputType::hasCreatedShadowSubtree const):
+ New functions to create the shadow subtree if it hasn't been done
+ already, and to query whether it's been done.
+
+ * html/HTMLInputElement.h:
+ * html/HTMLInputElement.cpp:
+ (WebCore::HTMLInputElement::innerTextElementCreatingShadowSubtreeIfNeeded):
+ * html/HTMLTextAreaElement.h:
+ * html/HTMLTextAreaElement.cpp:
+ (WebCore::HTMLTextAreaElement::innerTextElementCreatingShadowSubtreeIfNeeded):
+ * html/HTMLTextFormControlElement.h:
+ * html/InputType.h:
+ * html/InputType.cpp:
+ (WebCore::InputType::innerTextElementCreatingShadowSubtreeIfNeeded):
+ New functions to first create the shadow subtree before returning
+ innerTextElement(). HTMLTextAreaElement never lazily creates its
+ shadow subtree and so just returns innerTextElement().
+
+ * html/HTMLInputElement.h:
+ * html/HTMLInputElement.cpp:
+ (WebCore::HTMLInputElement::createShadowSubtreeAndUpdateInnerTextElementEditability):
+ Deleted. Just call through to m_inputType->createShadowTree()
+ directly.
+
+ (WebCore::HTMLInputElement::HTMLInputElement):
+ (WebCore::HTMLInputElement::create):
+ (WebCore::HTMLInputElement::initializeInputType):
+ (WebCore::HTMLInputElement::updateType):
+ Don't immediately create the shadow tree.
+
+ (WebCore::HTMLInputElement::didFinishInsertingNode):
+ Create the shadow subtree now that the element's been inserted. No
+ need to call dataListMayHaveChanged since
+ TextFieldInputType::createShadowSubtree will now do this.
+
+ * html/BaseDateAndTimeInputType.cpp:
+ (WebCore::BaseDateAndTimeInputType::updateInnerTextValue):
+ Ensure the shadow subtree is created since we need to poke at it.
+
+ * html/HTMLTextFormControlElement.cpp:
+ (WebCore::HTMLTextFormControlElement::forwardEvent):
+ Don't forward the event if the shadow tree hasn't been created yet.
+
+ (WebCore::HTMLTextFormControlElement::setSelectionRange):
+ Ensure the shadow tree has been created. This is needed if the
+ selection APIs are called on the input element before it's inserted
+ into the document.
+
+ (WebCore::HTMLTextFormControlElement::visiblePositionForIndex const):
+ Assert that the shadow tree has been created, since editing
+ functionality should only be needed if the element's been inserted
+ into the document.
+
+ (WebCore::HTMLTextFormControlElement::setInnerTextValue):
+ Ensure the shadow tree has been created.
+
+ * html/RangeInputType.cpp:
+ (WebCore::RangeInputType::handleMouseDownEvent):
+ (WebCore::RangeInputType::handleTouchEvent):
+ (WebCore::RangeInputType::handleKeydownEvent):
+ Ensure the shadow tree has been created in case the event will change
+ the value.
+
+ (WebCore::RangeInputType::sliderTrackElement const):
+ Only return the element if it's been created.
+
+ (WebCore::RangeInputType::typedSliderThumbElement const):
+ Assert that the element has been created.
+
+ (WebCore::RangeInputType::dataListMayHaveChanged):
+ Only try to re-layout if the shadow tree has been created.
+
+ * html/TextFieldInputType.cpp:
+ (WebCore::TextFieldInputType::isEmptyValue const):
+ Avoid creating the shadow subtree.
+
+ (WebCore::TextFieldInputType::forwardEvent):
+ Move the element assertion up to be consistent with other functions.
+
+ (WebCore::TextFieldInputType::innerTextElement const):
+ Don't assert, since this now can legitimately return null.
+
+ * html/FileInputType.cpp:
+ (WebCore::FileInputType::disabledStateChanged):
+ (WebCore::FileInputType::attributeChanged):
+ * html/RangeInputType.cpp:
+ (WebCore::RangeInputType::disabledStateChanged):
+ (WebCore::RangeInputType::attributeChanged):
+ (WebCore::RangeInputType::setValue):
+ * html/TextFieldInputType.cpp:
+ (WebCore::TextFieldInputType::disabledStateChanged):
+ (WebCore::TextFieldInputType::readOnlyStateChanged):
+ (WebCore::TextFieldInputType::updatePlaceholderText):
+ (WebCore::TextFieldInputType::updateAutoFillButton):
+ (WebCore::TextFieldInputType::dataListMayHaveChanged):
+ Don't update the shadow tree contents if it hasn't been created yet.
+ createShadowTree is responsible for ensuring it creates the shadow
+ tree contents reflecting the current state.
+
2022-02-21 Wenson Hsieh <wenson_hs...@apple.com>
Add test coverage for the pasteboard writing codepath added in r289839
Modified: trunk/Source/WebCore/html/BaseDateAndTimeInputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/BaseDateAndTimeInputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/BaseDateAndTimeInputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -306,7 +306,7 @@
}
}
-void BaseDateAndTimeInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool)
+void BaseDateAndTimeInputType::createShadowSubtree()
{
ASSERT(needsShadowSubtree());
ASSERT(element());
@@ -334,6 +334,9 @@
void BaseDateAndTimeInputType::updateInnerTextValue()
{
ASSERT(element());
+
+ createShadowSubtreeIfNeeded();
+
if (!m_dateTimeEditElement) {
auto node = element()->userAgentShadowRoot()->firstChild();
if (!is<HTMLElement>(node))
Modified: trunk/Source/WebCore/html/BaseDateAndTimeInputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/BaseDateAndTimeInputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/BaseDateAndTimeInputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -114,7 +114,7 @@
bool isMouseFocusable() const final;
void handleDOMActivateEvent(Event&) override;
- void createShadowSubtreeAndUpdateInnerTextElementEditability(bool) final;
+ void createShadowSubtree() final;
void destroyShadowSubtree() final;
void updateInnerTextValue() final;
bool hasCustomFocusLogic() const final;
Modified: trunk/Source/WebCore/html/ColorInputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/ColorInputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/ColorInputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -136,7 +136,7 @@
return parseSimpleColorValue(element()->value()).value();
}
-void ColorInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool)
+void ColorInputType::createShadowSubtree()
{
ASSERT(needsShadowSubtree());
ASSERT(element());
Modified: trunk/Source/WebCore/html/ColorInputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/ColorInputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/ColorInputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -62,7 +62,7 @@
bool supportsRequired() const final;
String fallbackValue() const final;
String sanitizeValue(const String&) const final;
- void createShadowSubtreeAndUpdateInnerTextElementEditability(bool) final;
+ void createShadowSubtree() final;
void setValue(const String&, bool valueChanged, TextFieldEventBehavior) final;
void attributeChanged(const QualifiedName&) final;
void handleDOMActivateEvent(Event&) final;
Modified: trunk/Source/WebCore/html/FileInputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/FileInputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/FileInputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -262,18 +262,20 @@
element()->invalidateStyleForSubtree();
}
-void FileInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool)
+void FileInputType::createShadowSubtree()
{
ASSERT(needsShadowSubtree());
ASSERT(element());
ASSERT(element()->shadowRoot());
- element()->userAgentShadowRoot()->appendChild(ContainerNode::ChildChange::Source::Parser, element()->multiple() ? UploadButtonElement::createForMultiple(element()->document()): UploadButtonElement::create(element()->document()));
+
+ auto button = element()->multiple() ? UploadButtonElement::createForMultiple(element()->document()) : UploadButtonElement::create(element()->document());
+ element()->userAgentShadowRoot()->appendChild(ContainerNode::ChildChange::Source::Parser, button);
+ disabledStateChanged();
}
void FileInputType::disabledStateChanged()
{
ASSERT(element());
- ASSERT(element()->shadowRoot());
auto root = element()->userAgentShadowRoot();
if (!root)
@@ -287,7 +289,6 @@
{
if (name == multipleAttr) {
if (auto* element = this->element()) {
- ASSERT(element->shadowRoot());
if (auto root = element->userAgentShadowRoot()) {
if (RefPtr button = childrenOfType<UploadButtonElement>(*root).first())
button->setValue(element->multiple() ? fileButtonChooseMultipleFilesLabel() : fileButtonChooseFileLabel());
Modified: trunk/Source/WebCore/html/FileInputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/FileInputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/FileInputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -77,7 +77,7 @@
#endif
Icon* icon() const final;
- void createShadowSubtreeAndUpdateInnerTextElementEditability(bool) final;
+ void createShadowSubtree() final;
void disabledStateChanged() final;
void attributeChanged(const QualifiedName&) final;
String defaultToolTip() const final;
Modified: trunk/Source/WebCore/html/HTMLInputElement.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/HTMLInputElement.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/HTMLInputElement.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -136,8 +136,8 @@
#endif
, m_isSpellcheckDisabledExceptTextReplacement(false)
{
- // m_inputType is lazily created when constructed by the parser to avoid constructing unnecessarily a text inputType and
- // its shadow subtree, just to destroy them when the |type| attribute gets set by the parser to something else than 'text'.
+ // 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'.
if (!createdByParser)
m_inputType = InputType::createText(*this);
@@ -147,14 +147,7 @@
Ref<HTMLInputElement> HTMLInputElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
{
- bool shouldCreateShadowRootLazily = createdByParser;
- Ref<HTMLInputElement> inputElement = adoptRef(*new HTMLInputElement(tagName, document, form, createdByParser));
- if (!shouldCreateShadowRootLazily) {
- ASSERT(inputElement->m_inputType->needsShadowSubtree());
- inputElement->createUserAgentShadowRoot();
- inputElement->createShadowSubtreeAndUpdateInnerTextElementEditability();
- }
- return inputElement;
+ return adoptRef(*new HTMLInputElement(tagName, document, form, createdByParser));
}
HTMLImageLoader& HTMLInputElement::ensureImageLoader()
@@ -164,12 +157,6 @@
return *m_imageLoader;
}
-void HTMLInputElement::createShadowSubtreeAndUpdateInnerTextElementEditability()
-{
- Ref<InputType> protectedInputType(*m_inputType);
- protectedInputType->createShadowSubtreeAndUpdateInnerTextElementEditability(isInnerTextElementEditable());
-}
-
HTMLInputElement::~HTMLInputElement()
{
if (needsSuspensionCallback())
@@ -212,6 +199,11 @@
return m_inputType->innerTextElement();
}
+RefPtr<TextControlInnerTextElement> HTMLInputElement::innerTextElementCreatingShadowSubtreeIfNeeded()
+{
+ return m_inputType->innerTextElementCreatingShadowSubtreeIfNeeded();
+}
+
HTMLElement* HTMLInputElement::innerBlockElement() const
{
return m_inputType->innerBlockElement();
@@ -580,10 +572,7 @@
m_inputType->detachFromElement();
m_inputType = WTFMove(newType);
- if (m_inputType->needsShadowSubtree()) {
- ensureUserAgentShadowRoot();
- createShadowSubtreeAndUpdateInnerTextElementEditability();
- }
+ m_inputType->createShadowSubtreeIfNeeded();
updateWillValidateAndValidity();
@@ -736,9 +725,6 @@
const AtomString& type = attributeWithoutSynchronization(typeAttr);
if (type.isNull()) {
m_inputType = InputType::createText(*this);
- ASSERT(m_inputType->needsShadowSubtree());
- createUserAgentShadowRoot();
- createShadowSubtreeAndUpdateInnerTextElementEditability();
updateWillValidateAndValidity();
return;
}
@@ -745,10 +731,6 @@
m_hasType = true;
m_inputType = InputType::create(*this, type);
- if (m_inputType->needsShadowSubtree()) {
- createUserAgentShadowRoot();
- createShadowSubtreeAndUpdateInnerTextElementEditability();
- }
updateWillValidateAndValidity();
registerForSuspensionCallbackIfNeeded();
runPostTypeUpdateTasks();
@@ -1599,10 +1581,8 @@
HTMLTextFormControlElement::didFinishInsertingNode();
if (isInTreeScope() && !form())
addToRadioButtonGroup();
-#if ENABLE(DATALIST_ELEMENT)
- if (isConnected() && m_hasNonEmptyList)
- dataListMayHaveChanged();
-#endif
+ if (isConnected())
+ m_inputType->createShadowSubtreeIfNeeded();
}
void HTMLInputElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
Modified: trunk/Source/WebCore/html/HTMLInputElement.h (290283 => 290284)
--- trunk/Source/WebCore/html/HTMLInputElement.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/HTMLInputElement.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -137,6 +137,7 @@
HTMLElement* containerElement() const;
RefPtr<TextControlInnerTextElement> innerTextElement() const final;
+ RefPtr<TextControlInnerTextElement> innerTextElementCreatingShadowSubtreeIfNeeded() final;
RenderStyle createInnerTextStyle(const RenderStyle&) final;
HTMLElement* innerBlockElement() const;
@@ -356,6 +357,8 @@
String resultForDialogSubmit() const final;
+ bool isInnerTextElementEditable() const final { return !hasAutoFillStrongPasswordButton() && HTMLTextFormControlElement::isInnerTextElementEditable(); }
+
protected:
HTMLInputElement(const QualifiedName&, Document&, HTMLFormElement*, bool createdByParser);
@@ -371,8 +374,6 @@
void removedFromAncestor(RemovalType, ContainerNode&) final;
void didMoveToNewDocument(Document& oldDocument, Document& newDocument) final;
- void createShadowSubtreeAndUpdateInnerTextElementEditability();
-
int defaultTabIndex() const final;
bool hasCustomFocusLogic() const final;
bool isKeyboardFocusable(KeyboardEvent*) const final;
@@ -384,8 +385,6 @@
bool isInteractiveContent() const final;
- bool isInnerTextElementEditable() const final { return !hasAutoFillStrongPasswordButton() && HTMLTextFormControlElement::isInnerTextElementEditable(); }
-
bool canTriggerImplicitSubmission() const final { return isTextField(); }
const AtomString& formControlType() const final;
Modified: trunk/Source/WebCore/html/HTMLTextAreaElement.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/HTMLTextAreaElement.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/HTMLTextAreaElement.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -345,6 +345,11 @@
return childrenOfType<TextControlInnerTextElement>(*root).first();
}
+RefPtr<TextControlInnerTextElement> HTMLTextAreaElement::innerTextElementCreatingShadowSubtreeIfNeeded()
+{
+ return innerTextElement();
+}
+
void HTMLTextAreaElement::rendererWillBeDestroyed()
{
updateValue();
Modified: trunk/Source/WebCore/html/HTMLTextAreaElement.h (290283 => 290284)
--- trunk/Source/WebCore/html/HTMLTextAreaElement.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/HTMLTextAreaElement.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -57,6 +57,7 @@
bool isValidValue(const String&) const;
WEBCORE_EXPORT RefPtr<TextControlInnerTextElement> innerTextElement() const final;
+ WEBCORE_EXPORT RefPtr<TextControlInnerTextElement> innerTextElementCreatingShadowSubtreeIfNeeded() final;
RenderStyle createInnerTextStyle(const RenderStyle&) final;
void copyNonAttributePropertiesFromElement(const Element&) final;
Modified: trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -157,7 +157,9 @@
{
if (event.type() == eventNames().blurEvent || event.type() == eventNames().focusEvent)
return;
- innerTextElement()->defaultEventHandler(event);
+
+ if (auto innerText = innerTextElement())
+ innerText->defaultEventHandler(event);
}
static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
@@ -309,7 +311,7 @@
end = std::max(end, 0);
start = std::min(std::max(start, 0), end);
- auto innerText = innerTextElement();
+ auto innerText = innerTextElementCreatingShadowSubtreeIfNeeded();
bool hasFocus = document().focusedElement() == this;
if (!hasFocus && innerText) {
if (!isConnected()) {
@@ -369,6 +371,7 @@
VisiblePosition HTMLTextFormControlElement::visiblePositionForIndex(int index) const
{
+ ASSERT(innerTextElement());
VisiblePosition position = positionForIndex(innerTextElement().get(), index);
ASSERT(indexForVisiblePosition(position) == index);
return position;
@@ -587,7 +590,7 @@
void HTMLTextFormControlElement::setInnerTextValue(const String& value)
{
LayoutDisallowedScope layoutDisallowedScope(LayoutDisallowedScope::Reason::PerformanceOptimization);
- auto innerText = innerTextElement();
+ auto innerText = innerTextElementCreatingShadowSubtreeIfNeeded();
if (!innerText)
return;
Modified: trunk/Source/WebCore/html/HTMLTextFormControlElement.h (290283 => 290284)
--- trunk/Source/WebCore/html/HTMLTextFormControlElement.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/HTMLTextFormControlElement.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -89,6 +89,7 @@
virtual String value() const = 0;
virtual RefPtr<TextControlInnerTextElement> innerTextElement() const = 0;
+ virtual RefPtr<TextControlInnerTextElement> innerTextElementCreatingShadowSubtreeIfNeeded() = 0;
virtual RenderStyle createInnerTextStyle(const RenderStyle&) = 0;
void selectionChanged(bool shouldFireSelectEvent);
Modified: trunk/Source/WebCore/html/InputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/InputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/InputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -561,7 +561,7 @@
element()->defaultBlur();
}
-void InputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool)
+void InputType::createShadowSubtree()
{
}
@@ -1119,6 +1119,12 @@
return nullptr;
}
+RefPtr<TextControlInnerTextElement> InputType::innerTextElementCreatingShadowSubtreeIfNeeded()
+{
+ createShadowSubtreeIfNeeded();
+ return innerTextElement();
+}
+
String InputType::resultForDialogSubmit() const
{
ASSERT(element());
@@ -1125,4 +1131,14 @@
return element()->value();
}
+void InputType::createShadowSubtreeIfNeeded()
+{
+ if (m_hasCreatedShadowSubtree || !needsShadowSubtree())
+ return;
+ Ref protectedThis { *this };
+ element()->ensureUserAgentShadowRoot();
+ m_hasCreatedShadowSubtree = true;
+ createShadowSubtree();
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/html/InputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/InputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/InputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -206,6 +206,7 @@
bool supportLabels() const;
bool isEnumeratable() const;
bool needsShadowSubtree() const { return !nonShadowRootTypes.contains(m_type); }
+ bool hasCreatedShadowSubtree() const { return m_hasCreatedShadowSubtree; }
// Form value functions.
@@ -306,7 +307,8 @@
// Shadow tree handling.
- virtual void createShadowSubtreeAndUpdateInnerTextElementEditability(bool);
+ void createShadowSubtreeIfNeeded();
+ virtual void createShadowSubtree();
virtual void destroyShadowSubtree();
virtual HTMLElement* containerElement() const { return nullptr; }
@@ -323,6 +325,7 @@
#if ENABLE(DATALIST_ELEMENT)
virtual HTMLElement* dataListButtonElement() const { return nullptr; }
#endif
+ RefPtr<TextControlInnerTextElement> innerTextElementCreatingShadowSubtreeIfNeeded();
// Miscellaneous functions.
@@ -413,6 +416,7 @@
ExceptionOr<void> applyStep(int count, AnyStepHandling, TextFieldEventBehavior);
const Type m_type;
+ bool m_hasCreatedShadowSubtree { false };
// m_element is null if this InputType is no longer associated with an element (either the element died or changed input type).
WeakPtr<HTMLInputElement> m_element;
};
Modified: trunk/Source/WebCore/html/RangeInputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/RangeInputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/RangeInputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -133,6 +133,10 @@
void RangeInputType::handleMouseDownEvent(MouseEvent& event)
{
ASSERT(element());
+
+ if (!hasCreatedShadowSubtree())
+ return;
+
if (element()->isDisabledFormControl())
return;
@@ -151,10 +155,15 @@
#if ENABLE(TOUCH_EVENTS)
void RangeInputType::handleTouchEvent(TouchEvent& event)
{
+ ASSERT(element());
+
+ if (!hasCreatedShadowSubtree())
+ return;
+
#if PLATFORM(IOS_FAMILY)
typedSliderThumbElement().handleTouchEvent(event);
#elif ENABLE(TOUCH_SLIDER)
- ASSERT(element());
+
if (element()->isDisabledFormControl())
return;
@@ -183,6 +192,8 @@
void RangeInputType::disabledStateChanged()
{
+ if (!hasCreatedShadowSubtree())
+ return;
typedSliderThumbElement().hostDisabledStateChanged();
}
@@ -189,6 +200,10 @@
auto RangeInputType::handleKeydownEvent(KeyboardEvent& event) -> ShouldCallBaseEventHandler
{
ASSERT(element());
+
+ if (!hasCreatedShadowSubtree())
+ return ShouldCallBaseEventHandler::Yes;
+
if (element()->isDisabledFormControl())
return ShouldCallBaseEventHandler::Yes;
@@ -241,7 +256,7 @@
return ShouldCallBaseEventHandler::Yes;
}
-void RangeInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool)
+void RangeInputType::createShadowSubtree()
{
ASSERT(needsShadowSubtree());
ASSERT(element());
@@ -259,6 +274,10 @@
HTMLElement* RangeInputType::sliderTrackElement() const
{
ASSERT(element());
+
+ if (!hasCreatedShadowSubtree())
+ return nullptr;
+
ASSERT(element()->userAgentShadowRoot());
ASSERT(element()->userAgentShadowRoot()->firstChild()); // container
ASSERT(element()->userAgentShadowRoot()->firstChild()->isHTMLElement());
@@ -277,6 +296,7 @@
SliderThumbElement& RangeInputType::typedSliderThumbElement() const
{
+ ASSERT(hasCreatedShadowSubtree());
ASSERT(sliderTrackElement()->firstChild()); // thumb
ASSERT(sliderTrackElement()->firstChild()->isHTMLElement());
@@ -322,7 +342,8 @@
if (element->hasDirtyValue())
element->setValue(element->value());
}
- typedSliderThumbElement().setPositionFromValue();
+ if (hasCreatedShadowSubtree())
+ typedSliderThumbElement().setPositionFromValue();
}
InputType::attributeChanged(name);
}
@@ -339,7 +360,8 @@
element()->setTextAsOfLastFormControlChangeEvent(value);
}
- typedSliderThumbElement().setPositionFromValue();
+ if (hasCreatedShadowSubtree())
+ typedSliderThumbElement().setPositionFromValue();
}
String RangeInputType::fallbackValue() const
@@ -368,7 +390,7 @@
{
m_tickMarkValuesDirty = true;
RefPtr<HTMLElement> sliderTrackElement = this->sliderTrackElement();
- if (sliderTrackElement->renderer())
+ if (sliderTrackElement && sliderTrackElement->renderer())
sliderTrackElement->renderer()->setNeedsLayout();
}
Modified: trunk/Source/WebCore/html/RangeInputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/RangeInputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/RangeInputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -52,7 +52,7 @@
void handleMouseDownEvent(MouseEvent&) final;
ShouldCallBaseEventHandler handleKeydownEvent(KeyboardEvent&) final;
RenderPtr<RenderElement> createInputRenderer(RenderStyle&&) final;
- void createShadowSubtreeAndUpdateInnerTextElementEditability(bool) final;
+ void createShadowSubtree() final;
Decimal parseToNumber(const String&, const Decimal&) const final;
String serialize(const Decimal&) const final;
bool accessKeyAction(bool sendMouseEvents) final;
Modified: trunk/Source/WebCore/html/SearchInputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/SearchInputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/SearchInputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -102,13 +102,13 @@
return true;
}
-void SearchInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool isInnerTextElementEditable)
+void SearchInputType::createShadowSubtree()
{
ASSERT(needsShadowSubtree());
ASSERT(!m_resultsButton);
ASSERT(!m_cancelButton);
- TextFieldInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(isInnerTextElementEditable);
+ TextFieldInputType::createShadowSubtree();
RefPtr<HTMLElement> container = containerElement();
RefPtr<HTMLElement> textWrapper = innerBlockElement();
ASSERT(container);
Modified: trunk/Source/WebCore/html/SearchInputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/SearchInputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/SearchInputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -51,7 +51,7 @@
RenderPtr<RenderElement> createInputRenderer(RenderStyle&&) final;
const AtomString& formControlType() const final;
bool needsContainer() const final;
- void createShadowSubtreeAndUpdateInnerTextElementEditability(bool) final;
+ void createShadowSubtree() final;
void destroyShadowSubtree() final;
HTMLElement* resultsButtonElement() const final;
HTMLElement* cancelButtonElement() const final;
Modified: trunk/Source/WebCore/html/TextFieldInputType.cpp (290283 => 290284)
--- trunk/Source/WebCore/html/TextFieldInputType.cpp 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/TextFieldInputType.cpp 2022-02-22 03:36:52 UTC (rev 290284)
@@ -108,7 +108,11 @@
bool TextFieldInputType::isEmptyValue() const
{
auto innerText = innerTextElement();
- ASSERT(innerText);
+ if (!innerText) {
+ // Since we always create the shadow subtree if a value is set, we know
+ // that the value is empty.
+ return true;
+ }
for (Text* text = TextNodeTraversal::firstWithin(*innerText); text; text = TextNodeTraversal::next(*text, innerText.get())) {
if (text->length())
@@ -221,6 +225,8 @@
void TextFieldInputType::forwardEvent(Event& event)
{
+ ASSERT(element());
+
if (m_innerSpinButton) {
m_innerSpinButton->forwardEvent(event);
if (event.defaultHandled())
@@ -231,10 +237,8 @@
bool isBlurEvent = event.type() == eventNames().blurEvent;
if (isFocusEvent || isBlurEvent)
capsLockStateMayHaveChanged();
- if (event.isMouseEvent() || isFocusEvent || isBlurEvent) {
- ASSERT(element());
+ if (event.isMouseEvent() || isFocusEvent || isBlurEvent)
element()->forwardEvent(event);
- }
}
void TextFieldInputType::elementDidBlur()
@@ -316,11 +320,12 @@
return RenderTheme::singleton().shouldHaveCapsLockIndicator(*element());
}
-void TextFieldInputType::createShadowSubtreeAndUpdateInnerTextElementEditability(bool isInnerTextElementEditable)
+void TextFieldInputType::createShadowSubtree()
{
ASSERT(needsShadowSubtree());
ASSERT(element());
ASSERT(element()->shadowRoot());
+ ASSERT(!element()->shadowRoot()->hasChildNodes());
ASSERT(!m_innerText);
ASSERT(!m_innerBlock);
@@ -331,9 +336,17 @@
Document& document = element()->document();
bool shouldHaveSpinButton = this->shouldHaveSpinButton();
bool shouldHaveCapsLockIndicator = this->shouldHaveCapsLockIndicator();
- bool createsContainer = shouldHaveSpinButton || shouldHaveCapsLockIndicator || needsContainer();
+ bool shouldDrawAutoFillButton = this->shouldDrawAutoFillButton();
+#if ENABLE(DATALIST_ELEMENT)
+ bool hasDataList = element()->list();
+#endif
+ bool createsContainer = shouldHaveSpinButton || shouldHaveCapsLockIndicator || shouldDrawAutoFillButton
+#if ENABLE(DATALIST_ELEMENT)
+ || hasDataList
+#endif
+ || needsContainer();
- m_innerText = TextControlInnerTextElement::create(document, isInnerTextElementEditable);
+ m_innerText = TextControlInnerTextElement::create(document, element()->isInnerTextElementEditable());
if (!createsContainer) {
element()->userAgentShadowRoot()->appendChild(ContainerNode::ChildChange::Source::Parser, *m_innerText);
@@ -358,7 +371,12 @@
m_container->appendChild(ContainerNode::ChildChange::Source::Parser, *m_capsLockIndicator);
}
+
updateAutoFillButton();
+
+#if ENABLE(DATALIST_ELEMENT)
+ dataListMayHaveChanged();
+#endif
}
HTMLElement* TextFieldInputType::containerElement() const
@@ -373,7 +391,6 @@
RefPtr<TextControlInnerTextElement> TextFieldInputType::innerTextElement() const
{
- ASSERT(m_innerText);
return m_innerText;
}
@@ -425,6 +442,9 @@
void TextFieldInputType::disabledStateChanged()
{
+ if (!hasCreatedShadowSubtree())
+ return;
+
if (m_innerSpinButton)
m_innerSpinButton->releaseCapture();
capsLockStateMayHaveChanged();
@@ -433,6 +453,9 @@
void TextFieldInputType::readOnlyStateChanged()
{
+ if (!hasCreatedShadowSubtree())
+ return;
+
if (m_innerSpinButton)
m_innerSpinButton->releaseCapture();
capsLockStateMayHaveChanged();
@@ -616,9 +639,14 @@
void TextFieldInputType::updatePlaceholderText()
{
+ ASSERT(element());
+
+ if (!hasCreatedShadowSubtree())
+ return;
+
if (!supportsPlaceholder())
return;
- ASSERT(element());
+
String placeholderText = element()->placeholder();
if (placeholderText.isEmpty()) {
if (m_placeholder) {
@@ -816,6 +844,11 @@
void TextFieldInputType::updateAutoFillButton()
{
+ ASSERT(element());
+
+ if (!hasCreatedShadowSubtree())
+ return;
+
capsLockStateMayHaveChanged();
if (shouldDrawAutoFillButton()) {
@@ -822,7 +855,6 @@
if (!m_container)
createContainer();
- ASSERT(element());
AutoFillButtonType autoFillButtonType = element()->autoFillButtonType();
if (!m_autoFillButton)
createAutoFillButton(autoFillButtonType);
@@ -846,6 +878,9 @@
void TextFieldInputType::dataListMayHaveChanged()
{
+ if (!hasCreatedShadowSubtree())
+ return;
+
m_cachedSuggestions = { };
if (!m_dataListDropdownIndicator)
Modified: trunk/Source/WebCore/html/TextFieldInputType.h (290283 => 290284)
--- trunk/Source/WebCore/html/TextFieldInputType.h 2022-02-22 02:16:28 UTC (rev 290283)
+++ trunk/Source/WebCore/html/TextFieldInputType.h 2022-02-22 03:36:52 UTC (rev 290284)
@@ -70,7 +70,7 @@
#endif
virtual bool needsContainer() const;
- void createShadowSubtreeAndUpdateInnerTextElementEditability(bool) override;
+ void createShadowSubtree() override;
void destroyShadowSubtree() override;
void attributeChanged(const QualifiedName&) override;
void disabledStateChanged() final;