diff --git a/Source/WTF/WTF.xcodeproj/project.pbxproj b/Source/WTF/WTF.xcodeproj/project.pbxproj index 0968515a29d9a..0a2e656f77666 100644 --- a/Source/WTF/WTF.xcodeproj/project.pbxproj +++ b/Source/WTF/WTF.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 14022F4118F5C3FC007FF0EB /* libbmalloc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14022F4018F5C3FC007FF0EB /* libbmalloc.a */; }; 143DDE9620C8BC37007F76FA /* Entitlements.mm in Sources */ = {isa = PBXBuildFile; fileRef = 143DDE9520C8BC37007F76FA /* Entitlements.mm */; }; 143F611F1565F0F900DB514A /* RAMSize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 143F611D1565F0F900DB514A /* RAMSize.cpp */; }; + 144177D62D89B7C100F5099E /* ValueOrReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 144177D52D89B7C100F5099E /* ValueOrReference.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1469419D16EAB10A0024E146 /* AutodrainedPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1469419B16EAB10A0024E146 /* AutodrainedPool.cpp */; }; 1A1D8B9E1731879800141DA4 /* FunctionDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A1D8B9D1731879800141DA4 /* FunctionDispatcher.cpp */; }; 1C181C7F1D3078DA00F5FA16 /* TextBreakIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C181C7D1D3078DA00F5FA16 /* TextBreakIterator.cpp */; }; @@ -1011,6 +1012,7 @@ 143DDE9720C8BE99007F76FA /* Entitlements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Entitlements.h; sourceTree = ""; }; 143F611D1565F0F900DB514A /* RAMSize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RAMSize.cpp; sourceTree = ""; }; 143F611E1565F0F900DB514A /* RAMSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RAMSize.h; sourceTree = ""; }; + 144177D52D89B7C100F5099E /* ValueOrReference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ValueOrReference.h; sourceTree = ""; }; 1447AEC518FCE57700B3D7FF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 1447AECA18FCE5B900B3D7FF /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = /usr/lib/libicucore.dylib; sourceTree = ""; }; 1469419416EAAFF80024E146 /* SchedulePair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchedulePair.h; sourceTree = ""; }; @@ -2310,6 +2312,7 @@ 7AFEC6B01EB22B5900DADE36 /* UUID.cpp */, 7AFEC6AE1EB22AC600DADE36 /* UUID.h */, A8A4736F151A825B004123FF /* ValueCheck.h */, + 144177D52D89B7C100F5099E /* ValueOrReference.h */, 0F95B63420CB53C100479635 /* Vector.cpp */, A8A47370151A825B004123FF /* Vector.h */, E419F2E623AB9E2300B26129 /* VectorHash.h */, @@ -3241,6 +3244,7 @@ DDF306F527C086CC006A526F /* utils.h in Headers */, DD3DC8C227A4BF8E007E5B61 /* UUID.h in Headers */, DD3DC86D27A4BF8E007E5B61 /* ValueCheck.h in Headers */, + 144177D62D89B7C100F5099E /* ValueOrReference.h in Headers */, DD3DC90B27A4BF8E007E5B61 /* Vector.h in Headers */, DDF3079727C086CD006A526F /* VectorCocoa.h in Headers */, DD3DC8B127A4BF8E007E5B61 /* VectorHash.h in Headers */, diff --git a/Source/WTF/wtf/CMakeLists.txt b/Source/WTF/wtf/CMakeLists.txt index 826237ba339d3..da443e79f0c42 100644 --- a/Source/WTF/wtf/CMakeLists.txt +++ b/Source/WTF/wtf/CMakeLists.txt @@ -318,6 +318,7 @@ set(WTF_PUBLIC_HEADERS UnsafePointer.h VMTags.h ValueCheck.h + ValueOrReference.h Vector.h VectorHash.h VectorTraits.h diff --git a/Source/WTF/wtf/Compiler.h b/Source/WTF/wtf/Compiler.h index 626f6f8162f3d..cbacbde15f43d 100644 --- a/Source/WTF/wtf/Compiler.h +++ b/Source/WTF/wtf/Compiler.h @@ -416,6 +416,16 @@ #define WARN_UNUSED_RETURN #endif +/* LIFETIME_BOUND */ + +#if !defined(LIFETIME_BOUND) && COMPILER(CLANG) +#define LIFETIME_BOUND [[clang::lifetimebound]] +#endif + +#if !defined(LIFETIME_BOUND) +#define LIFETIME_BOUND +#endif + /* DEBUGGER_ANNOTATION_MARKER */ #if !defined(DEBUGGER_ANNOTATION_MARKER) && COMPILER(GCC) diff --git a/Source/WTF/wtf/ValueOrReference.h b/Source/WTF/wtf/ValueOrReference.h new file mode 100644 index 0000000000000..b89d767332c5a --- /dev/null +++ b/Source/WTF/wtf/ValueOrReference.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2025 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace WTF { + +// ValueOrReference is just like const T&, except that it can also optionally hold T. + +// ValueOrReference is an optimization when you need to return a value that is +// usually an existing reference, but sometimes a temporary, e.g.: +// +// ValueOrReference append(const String& string LIFETIME_BOUND, std::optional suffix) +// { +// if (LIKELY(!suffix)) +// return string; // existing reference -- ValueOrReference avoids a copy +// return makeString(string, suffix.value()); // temporary -- ValueOrReference holds T +// } +template class ValueOrReference { +public: + ValueOrReference() + : m_reference(m_value) + { + } + + ValueOrReference(ValueOrReference&& other) + : m_value(WTFMove(other.m_value)) + , m_reference(&other.m_reference == &other.m_value ? m_value : other.m_reference) + { + } + + ValueOrReference(const T& reference LIFETIME_BOUND) + : m_reference(reference) + { + } + + ValueOrReference(T&& temporary) + : m_value(WTFMove(temporary)) + , m_reference(m_value) + { + } + + operator const T&() const LIFETIME_BOUND { return m_reference; } + const T& get() const LIFETIME_BOUND { return m_reference; } + const T* operator->() const LIFETIME_BOUND { return &m_reference; } + +private: + T m_value; + const T& m_reference; +}; + +} // namespace WTF + +using WTF::ValueOrReference; diff --git a/Source/WTF/wtf/Vector.h b/Source/WTF/wtf/Vector.h index 2657c669dd8bc..3311c0554cac7 100644 --- a/Source/WTF/wtf/Vector.h +++ b/Source/WTF/wtf/Vector.h @@ -20,6 +20,7 @@ #pragma once +#include #include #include #include @@ -1909,6 +1910,15 @@ inline auto copyToVector(const Collection& collection) -> Vector::Type>(collection); } +template static bool insertInUniquedSortedVector(Vector& vector, const T& value) +{ + auto it = std::lower_bound(vector.begin(), vector.end(), value); + if (UNLIKELY(it != vector.end() && *it == value)) + return false; + vector.insert(it - vector.begin(), value); + return true; +} + } // namespace WTF using WTF::UnsafeVectorOverflow; @@ -1917,4 +1927,5 @@ using WTF::copyToVector; using WTF::copyToVectorOf; using WTF::copyToVectorSpecialization; using WTF::compactMap; +using WTF::insertInUniquedSortedVector; using WTF::removeRepeatedElements; diff --git a/Source/WTF/wtf/text/StringImpl.cpp b/Source/WTF/wtf/text/StringImpl.cpp index c5aec34d9c610..c8d57040a3bd8 100644 --- a/Source/WTF/wtf/text/StringImpl.cpp +++ b/Source/WTF/wtf/text/StringImpl.cpp @@ -25,6 +25,7 @@ #include "config.h" #include +#include #include #include #include diff --git a/Source/WTF/wtf/text/StringImpl.h b/Source/WTF/wtf/text/StringImpl.h index b91e2410f8af4..f526ee37e7b93 100644 --- a/Source/WTF/wtf/text/StringImpl.h +++ b/Source/WTF/wtf/text/StringImpl.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -154,7 +155,7 @@ class STRING_IMPL_ALIGNMENT StringImplShape { template constexpr StringImplShape(unsigned refCount, unsigned length, const char (&characters)[characterCount], unsigned hashAndFlags, ConstructWithConstExprTag); template constexpr StringImplShape(unsigned refCount, unsigned length, const char16_t (&characters)[characterCount], unsigned hashAndFlags, ConstructWithConstExprTag); - unsigned m_refCount; + std::atomic m_refCount; unsigned m_length; union { const LChar* m_data8; @@ -350,11 +351,11 @@ class StringImpl : private StringImplShape { unsigned symbolAwareHash() const; unsigned existingSymbolAwareHash() const; - SUPPRESS_TSAN bool isStatic() const { return m_refCount & s_refCountFlagIsStaticString; } + SUPPRESS_TSAN bool isStatic() const { return m_refCount.load(std::memory_order_relaxed) & s_refCountFlagIsStaticString; } - size_t refCount() const { return m_refCount / s_refCountIncrement; } - bool hasOneRef() const { return m_refCount == s_refCountIncrement; } - bool hasAtLeastOneRef() const { return m_refCount; } // For assertions. + size_t refCount() const { return m_refCount.load(std::memory_order_relaxed) / s_refCountIncrement; } + bool hasOneRef() const { return m_refCount.load(std::memory_order_relaxed) == s_refCountIncrement; } + bool hasAtLeastOneRef() const { return m_refCount.load(std::memory_order_relaxed); } // For assertions. void ref(); void deref(); @@ -1104,7 +1105,7 @@ inline void StringImpl::ref() return; #endif - m_refCount += s_refCountIncrement; + m_refCount.fetch_add(s_refCountIncrement, std::memory_order_relaxed); } inline void StringImpl::deref() @@ -1116,12 +1117,11 @@ inline void StringImpl::deref() return; #endif - unsigned tempRefCount = m_refCount - s_refCountIncrement; - if (!tempRefCount) { - StringImpl::destroy(this); + auto oldRefCount = m_refCount.fetch_sub(s_refCountIncrement, std::memory_order_relaxed); + if (oldRefCount != s_refCountIncrement) return; - } - m_refCount = tempRefCount; + + StringImpl::destroy(this); } template diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp index 7536a4965a4a2..b6d086cb58b4b 100644 --- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -2100,7 +2100,7 @@ VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) co if (isNativeTextControl()) { auto& textControl = downcast(*m_renderer).textFormControlElement(); - return textControl.visiblePositionForIndex(std::clamp(index, 0, static_cast(textControl.value().length()))); + return textControl.visiblePositionForIndex(std::clamp(index, 0, static_cast(textControl.value()->length()))); } if (!allowsTextRanges() && !is(*m_renderer)) @@ -2309,13 +2309,13 @@ void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePos auto innerRange = makeVisiblePositionRange(AXObjectCache::rangeForNodeContents(*innerText)); if (range.start.equals(textControlRange.end)) - start = textControl->value().length(); + start = textControl->value()->length(); else if (range.start <= innerRange.start) start = 0; if (range.end >= innerRange.end || range.end.equals(textControlRange.end)) - end = textControl->value().length(); + end = textControl->value()->length(); } } diff --git a/Source/WebCore/accessibility/AccessibilitySlider.cpp b/Source/WebCore/accessibility/AccessibilitySlider.cpp index f84e99abea0b3..c5b27e307b417 100644 --- a/Source/WebCore/accessibility/AccessibilitySlider.cpp +++ b/Source/WebCore/accessibility/AccessibilitySlider.cpp @@ -122,7 +122,7 @@ AXCoreObject* AccessibilitySlider::elementAccessibilityHitTest(const IntPoint& p float AccessibilitySlider::valueForRange() const { if (auto* input = inputElement()) - return input->value().toFloat(); + return input->value()->toFloat(); return 0; } diff --git a/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm b/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm index ada63f6b3c7be..6db6fdae9e596 100644 --- a/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm +++ b/Source/WebCore/accessibility/ios/AccessibilityObjectIOS.mm @@ -90,7 +90,7 @@ return false; HTMLInputElement& inputElement = downcast(*renderObject->node()); - return inputElement.value().length(); + return inputElement.value()->length(); } bool AccessibilityObject::accessibilityIgnoreAttachment() const diff --git a/Source/WebCore/dom/Attribute.h b/Source/WebCore/dom/Attribute.h index f7e3f305b01bc..ad03b8a476c32 100644 --- a/Source/WebCore/dom/Attribute.h +++ b/Source/WebCore/dom/Attribute.h @@ -41,6 +41,12 @@ class Attribute { { } + Attribute(QualifiedName&& name, AtomString&& value) + : m_name(WTFMove(name)) + , m_value(WTFMove(value)) + { + } + // NOTE: The references returned by these functions are only valid for as long // as the Attribute stays in place. For example, calling a function that mutates // an Element's internal attribute storage may invalidate them. diff --git a/Source/WebCore/editing/cocoa/AutofillElements.cpp b/Source/WebCore/editing/cocoa/AutofillElements.cpp index 4a81c3808f2c8..0f1af8c22900c 100644 --- a/Source/WebCore/editing/cocoa/AutofillElements.cpp +++ b/Source/WebCore/editing/cocoa/AutofillElements.cpp @@ -89,7 +89,7 @@ std::optional AutofillElements::computeAutofillElements(RefisPasswordField(); - bool hasSecondPasswordFieldToFill = nextElement && nextElement->isPasswordField() && nextElement->value().isEmpty(); + bool hasSecondPasswordFieldToFill = nextElement && nextElement->isPasswordField() && nextElement->value()->isEmpty(); // Always allow AutoFill in a password field, even if we fill information only into it. return {{ previousFieldIsTextField ? WTFMove(previousElement) : nullptr, WTFMove(start), hasSecondPasswordFieldToFill ? WTFMove(nextElement) : nullptr }}; @@ -98,7 +98,7 @@ std::optional AutofillElements::computeAutofillElements(Ref(*nextElement)) { if (nextElement->isPasswordField()) { auto elementAfterNextElement = nextAutofillableElement(nextElement.get(), focusController); - bool hasSecondPasswordFieldToFill = elementAfterNextElement && elementAfterNextElement->isPasswordField() && elementAfterNextElement->value().isEmpty(); + bool hasSecondPasswordFieldToFill = elementAfterNextElement && elementAfterNextElement->isPasswordField() && elementAfterNextElement->value()->isEmpty(); return {{ WTFMove(start), WTFMove(nextElement), hasSecondPasswordFieldToFill ? WTFMove(elementAfterNextElement) : nullptr }}; } diff --git a/Source/WebCore/editing/cocoa/HTMLConverter.mm b/Source/WebCore/editing/cocoa/HTMLConverter.mm index 0a1e6736894f3..7d656a046c1dc 100644 --- a/Source/WebCore/editing/cocoa/HTMLConverter.mm +++ b/Source/WebCore/editing/cocoa/HTMLConverter.mm @@ -1867,7 +1867,7 @@ static NSInteger _colCompare(id block1, id block2, void *) if (is(element)) { HTMLInputElement& inputElement = downcast(element); if (inputElement.type() == textAtom()) { - NSString *value = inputElement.value(); + NSString *value = inputElement.value().get(); if (value && [value length] > 0) _addValue(value, element); } @@ -1875,7 +1875,7 @@ static NSInteger _colCompare(id block1, id block2, void *) } else if (element.hasTagName(textareaTag)) { if (is(element)) { HTMLTextAreaElement& textAreaElement = downcast(element); - NSString *value = textAreaElement.value(); + NSString *value = textAreaElement.value().get(); if (value && [value length] > 0) _addValue(value, element); } diff --git a/Source/WebCore/html/BaseCheckableInputType.cpp b/Source/WebCore/html/BaseCheckableInputType.cpp index fc1eac9622ad4..664a739e7560b 100644 --- a/Source/WebCore/html/BaseCheckableInputType.cpp +++ b/Source/WebCore/html/BaseCheckableInputType.cpp @@ -98,9 +98,9 @@ bool BaseCheckableInputType::accessKeyAction(bool sendMouseEvents) return InputType::accessKeyAction(sendMouseEvents) || element()->dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents); } -String BaseCheckableInputType::fallbackValue() const +ValueOrReference BaseCheckableInputType::fallbackValue() const { - return onAtom(); + return onAtom().string(); } bool BaseCheckableInputType::storesValueSeparateFromAttribute() diff --git a/Source/WebCore/html/BaseCheckableInputType.h b/Source/WebCore/html/BaseCheckableInputType.h index 425958a02a643..e04c2b1255a31 100644 --- a/Source/WebCore/html/BaseCheckableInputType.h +++ b/Source/WebCore/html/BaseCheckableInputType.h @@ -54,7 +54,7 @@ class BaseCheckableInputType : public InputType { bool appendFormData(DOMFormData&) const final; void handleKeypressEvent(KeyboardEvent&) final; bool accessKeyAction(bool sendMouseEvents) final; - String fallbackValue() const final; + ValueOrReference fallbackValue() const final; bool storesValueSeparateFromAttribute() final; void setValue(const String&, bool, TextFieldEventBehavior, TextControlSetValueSelection) final; }; diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.cpp b/Source/WebCore/html/BaseDateAndTimeInputType.cpp index 0d59def4c6f89..9c6240d6cd059 100644 --- a/Source/WebCore/html/BaseDateAndTimeInputType.cpp +++ b/Source/WebCore/html/BaseDateAndTimeInputType.cpp @@ -225,9 +225,11 @@ String BaseDateAndTimeInputType::visibleValue() const return localizeValue(element()->value()); } -String BaseDateAndTimeInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference BaseDateAndTimeInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { - return typeMismatchFor(proposedValue) ? String() : proposedValue; + if (typeMismatchFor(proposedValue)) + return String(); + return proposedValue; } bool BaseDateAndTimeInputType::supportsReadOnly() const @@ -355,7 +357,7 @@ void BaseDateAndTimeInputType::updateInnerTextValue() DateTimeEditElement::LayoutParameters layoutParameters(element()->locale()); - auto date = parseToDateComponents(element()->value()); + auto date = parseToDateComponents(element()->value().get()); if (date) setupLayoutParameters(layoutParameters, *date); else { @@ -553,7 +555,7 @@ bool BaseDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserPar parameters.isAnchorElementRTL = computedStyle->direction() == TextDirection::RTL; parameters.useDarkAppearance = document.useDarkAppearance(computedStyle); - auto date = valueOrDefault(parseToDateComponents(element.value())); + auto date = valueOrDefault(parseToDateComponents(element.value().get())); parameters.hasSecondField = shouldHaveSecondField(date); parameters.hasMillisecondField = shouldHaveMillisecondField(date); diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.h b/Source/WebCore/html/BaseDateAndTimeInputType.h index b56e67a33332b..57861dab83430 100644 --- a/Source/WebCore/html/BaseDateAndTimeInputType.h +++ b/Source/WebCore/html/BaseDateAndTimeInputType.h @@ -103,7 +103,7 @@ class BaseDateAndTimeInputType : public InputType, private DateTimeChooserClient // InputType functions: String visibleValue() const final; - String sanitizeValue(const String&) const override; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const override; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final; WallTime valueAsDate() const override; ExceptionOr setValueAsDate(WallTime) const override; diff --git a/Source/WebCore/html/ColorInputType.cpp b/Source/WebCore/html/ColorInputType.cpp index 6a9d397dfbc8b..2ada315d7fcb8 100644 --- a/Source/WebCore/html/ColorInputType.cpp +++ b/Source/WebCore/html/ColorInputType.cpp @@ -116,12 +116,12 @@ bool ColorInputType::supportsRequired() const return false; } -String ColorInputType::fallbackValue() const +ValueOrReference ColorInputType::fallbackValue() const { - return "#000000"_s; + return String { "#000000"_s }; } -String ColorInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference ColorInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (!isValidSimpleColor(proposedValue)) return fallbackValue(); @@ -132,7 +132,7 @@ String ColorInputType::sanitizeValue(const String& proposedValue) const Color ColorInputType::valueAsColor() const { ASSERT(element()); - return parseSimpleColorValue(element()->value()).value(); + return parseSimpleColorValue(element()->value().get()).value(); } void ColorInputType::createShadowSubtree() diff --git a/Source/WebCore/html/ColorInputType.h b/Source/WebCore/html/ColorInputType.h index df9fcb988d729..7644c4d3b6ebe 100644 --- a/Source/WebCore/html/ColorInputType.h +++ b/Source/WebCore/html/ColorInputType.h @@ -66,8 +66,8 @@ class ColorInputType final : public BaseClickableWithKeyInputType, private Color bool isPresentingAttachedView() const final; const AtomString& formControlType() const final; bool supportsRequired() const final; - String fallbackValue() const final; - String sanitizeValue(const String&) const final; + ValueOrReference fallbackValue() const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; void createShadowSubtree() final; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final; void attributeChanged(const QualifiedName&) final; diff --git a/Source/WebCore/html/DateTimeLocalInputType.cpp b/Source/WebCore/html/DateTimeLocalInputType.cpp index aeb4a12d195bf..0ab82023f1bb6 100644 --- a/Source/WebCore/html/DateTimeLocalInputType.cpp +++ b/Source/WebCore/html/DateTimeLocalInputType.cpp @@ -99,13 +99,15 @@ bool DateTimeLocalInputType::isValidFormat(OptionSet DateTimeLocalInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (proposedValue.isEmpty()) return proposedValue; - auto components = DateComponents::fromParsingDateTimeLocal(proposedValue); - return components ? components->toString() : emptyString(); + if (auto components = DateComponents::fromParsingDateTimeLocal(proposedValue)) + return components->toString(); + + return emptyString(); } String DateTimeLocalInputType::formatDateTimeFieldsState(const DateTimeFieldsState& state) const diff --git a/Source/WebCore/html/DateTimeLocalInputType.h b/Source/WebCore/html/DateTimeLocalInputType.h index 7d7f879610cc3..228183ea2ad03 100644 --- a/Source/WebCore/html/DateTimeLocalInputType.h +++ b/Source/WebCore/html/DateTimeLocalInputType.h @@ -52,7 +52,7 @@ class DateTimeLocalInputType final : public BaseDateAndTimeInputType { StepRange createStepRange(AnyStepHandling) const final; std::optional parseToDateComponents(StringView) const final; std::optional setMillisecondToDateComponents(double) const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; bool isValidFormat(OptionSet) const final; String formatDateTimeFieldsState(const DateTimeFieldsState&) const final; diff --git a/Source/WebCore/html/EmailInputType.cpp b/Source/WebCore/html/EmailInputType.cpp index 33533f6d53604..b54475f0a0e2c 100644 --- a/Source/WebCore/html/EmailInputType.cpp +++ b/Source/WebCore/html/EmailInputType.cpp @@ -88,7 +88,7 @@ bool EmailInputType::supportsSelectionAPI() const return false; } -String EmailInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference EmailInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { String noLineBreakValue = proposedValue.removeCharacters(isHTMLLineBreak); ASSERT(element()); diff --git a/Source/WebCore/html/EmailInputType.h b/Source/WebCore/html/EmailInputType.h index 56c79b27df609..f421164b1dda9 100644 --- a/Source/WebCore/html/EmailInputType.h +++ b/Source/WebCore/html/EmailInputType.h @@ -49,7 +49,7 @@ class EmailInputType final : public BaseTextInputType { bool typeMismatch() const final; String typeMismatchText() const final; bool supportsSelectionAPI() const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; }; } // namespace WebCore diff --git a/Source/WebCore/html/HTMLElement.cpp b/Source/WebCore/html/HTMLElement.cpp index c5f9197f07a6b..5f70875841bab 100644 --- a/Source/WebCore/html/HTMLElement.cpp +++ b/Source/WebCore/html/HTMLElement.cpp @@ -818,7 +818,7 @@ TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) c auto* inputElement = dynamicDowncast(textControl); if (!inputElement || (inputElement->isTextType() && !inputElement->isPasswordField())) { bool hasStrongDirectionality; - UCharDirection textDirection = textControl->value().defaultWritingDirection(&hasStrongDirectionality); + UCharDirection textDirection = textControl->value()->defaultWritingDirection(&hasStrongDirectionality); if (strongDirectionalityTextNode) *strongDirectionalityTextNode = hasStrongDirectionality ? textControl : nullptr; return (textDirection == U_LEFT_TO_RIGHT) ? TextDirection::LTR : TextDirection::RTL; diff --git a/Source/WebCore/html/HTMLInputElement.cpp b/Source/WebCore/html/HTMLInputElement.cpp index ab3f945eba846..e3e0b122ad0db 100644 --- a/Source/WebCore/html/HTMLInputElement.cpp +++ b/Source/WebCore/html/HTMLInputElement.cpp @@ -240,12 +240,12 @@ bool HTMLInputElement::isValidValue(const String& value) const bool HTMLInputElement::tooShort() const { - return tooShort(value(), CheckDirtyFlag); + return tooShort(value().get(), CheckDirtyFlag); } bool HTMLInputElement::tooLong() const { - return tooLong(value(), CheckDirtyFlag); + return tooLong(value().get(), CheckDirtyFlag); } bool HTMLInputElement::typeMismatch() const @@ -343,8 +343,8 @@ bool HTMLInputElement::stepMismatch() const bool HTMLInputElement::computeValidity() const { - String value = this->value(); - bool someError = m_inputType->isInvalid(value) || tooShort(value, CheckDirtyFlag) || tooLong(value, CheckDirtyFlag) || customError(); + auto value = this->value(); + bool someError = m_inputType->isInvalid(value) || tooShort(value.get(), CheckDirtyFlag) || tooLong(value.get(), CheckDirtyFlag) || customError(); return !someError; } @@ -370,7 +370,7 @@ std::optional HTMLInputElement::listOptionValueAsDouble(const HTMLOption if (!isValidValue(optionValue)) return std::nullopt; - return parseToDoubleForNumberType(sanitizeValue(optionValue)); + return parseToDoubleForNumberType(sanitizeValue(optionValue).get()); } #endif @@ -1009,7 +1009,7 @@ void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& sour m_inputType->updateInnerTextValue(); } -String HTMLInputElement::value() const +ValueOrReference HTMLInputElement::value() const { if (auto* fileInput = dynamicDowncast(*m_inputType)) return fileInput->firstElementPathForInputValue(); @@ -1018,7 +1018,7 @@ String HTMLInputElement::value() const return m_valueIfDirty; if (auto& valueString = attributeWithoutSynchronization(valueAttr); !valueString.isNull()) { - if (auto sanitizedValue = sanitizeValue(valueString); !sanitizedValue.isNull()) + if (auto sanitizedValue = sanitizeValue(valueString); !sanitizedValue->isNull()) return sanitizedValue; } @@ -1027,7 +1027,7 @@ String HTMLInputElement::value() const String HTMLInputElement::valueWithDefault() const { - if (auto value = this->value(); !value.isNull()) + if (auto value = this->value(); !value->isNull()) return value; return m_inputType->defaultValue(); @@ -1045,7 +1045,7 @@ ExceptionOr HTMLInputElement::setValue(const String& value, TextFieldEvent setLastChangeWasNotUserEdit(); setFormControlValueMatchesRenderer(false); - m_inputType->setValue(WTFMove(sanitizedValue), valueChanged, eventBehavior, selection); + m_inputType->setValue(sanitizedValue, valueChanged, eventBehavior, selection); bool wasModifiedProgrammatically = eventBehavior == DispatchNoEvent; if (wasModifiedProgrammatically) { @@ -1096,7 +1096,7 @@ void HTMLInputElement::setValueFromRenderer(const String& value) // Input types that support the selection API do *not* sanitize their // user input in order to retain parity between what's in the model and // what's on the screen. - ASSERT(m_inputType->supportsSelectionAPI() || value == sanitizeValue(value) || sanitizeValue(value).isEmpty()); + ASSERT(m_inputType->supportsSelectionAPI() || value == sanitizeValue(value) || sanitizeValue(value)->isEmpty()); // Workaround for bug where trailing \n is included in the result of textContent. // The assert macro above may also be simplified by removing the expression @@ -1450,7 +1450,7 @@ String HTMLInputElement::visibleValue() const return m_inputType->visibleValue(); } -String HTMLInputElement::sanitizeValue(const String& proposedValue) const +ValueOrReference HTMLInputElement::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (proposedValue.isNull()) return proposedValue; @@ -1895,7 +1895,7 @@ void HTMLInputElement::minLengthAttributeChanged(const AtomString& newValue) void HTMLInputElement::updateValueIfNeeded() { auto newValue = sanitizeValue(m_valueIfDirty); - ASSERT(!m_valueIfDirty.isNull() || newValue.isNull()); + ASSERT(!m_valueIfDirty.isNull() || newValue->isNull()); if (newValue != m_valueIfDirty) setValue(newValue); } diff --git a/Source/WebCore/html/HTMLInputElement.h b/Source/WebCore/html/HTMLInputElement.h index eb23d9e3c52aa..81f2e3b8ce73e 100644 --- a/Source/WebCore/html/HTMLInputElement.h +++ b/Source/WebCore/html/HTMLInputElement.h @@ -26,6 +26,7 @@ #include "HTMLTextFormControlElement.h" #include +#include namespace WebCore { @@ -81,7 +82,7 @@ class HTMLInputElement : public HTMLTextFormControlElement { WEBCORE_EXPORT const AtomString& defaultValue() const; WEBCORE_EXPORT void setDefaultValue(const AtomString&); WEBCORE_EXPORT void setType(const AtomString&); - WEBCORE_EXPORT String value() const final; + WEBCORE_EXPORT ValueOrReference value() const final; WEBCORE_EXPORT ExceptionOr setValue(const String&, TextFieldEventBehavior = DispatchNoEvent, TextControlSetValueSelection = TextControlSetValueSelection::SetSelectionToEnd) final; void setValueForUser(const String& value) { setValue(value, DispatchInputAndChangeEvent); } WEBCORE_EXPORT WallTime valueAsDate() const; @@ -201,7 +202,7 @@ class HTMLInputElement : public HTMLTextFormControlElement { String placeholder() const; - String sanitizeValue(const String&) const; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const; String localizeValue(const String&) const; diff --git a/Source/WebCore/html/HTMLTextAreaElement.cpp b/Source/WebCore/html/HTMLTextAreaElement.cpp index 36bf1310190e5..dc61589f7c9f7 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.cpp +++ b/Source/WebCore/html/HTMLTextAreaElement.cpp @@ -128,7 +128,7 @@ void HTMLTextAreaElement::childrenChanged(const ChildChange& change) HTMLElement::childrenChanged(change); setLastChangeWasNotUserEdit(); if (m_isDirty) - setInnerTextValue(value()); + setInnerTextValue(String { value() }); else setNonDirtyValue(defaultValue(), TextControlSetValueSelection::Clamp); } @@ -213,7 +213,7 @@ bool HTMLTextAreaElement::appendFormData(DOMFormData& formData) Ref protectedThis(*this); document().updateLayout(); - formData.append(name(), m_wrap == HardWrap ? valueWithHardLineBreaks() : value()); + formData.append(name(), m_wrap == HardWrap ? valueWithHardLineBreaks() : value().get()); if (auto& dirname = attributeWithoutSynchronization(dirnameAttr); !dirname.isNull()) formData.append(dirname, directionForFormData()); return true; @@ -317,7 +317,7 @@ void HTMLTextAreaElement::updateValue() const const_cast(this)->updatePlaceholderVisibility(); } -String HTMLTextAreaElement::value() const +ValueOrReference HTMLTextAreaElement::value() const { updateValue(); return m_value; @@ -371,7 +371,7 @@ void HTMLTextAreaElement::setValueCommon(const String& newValue, TextFieldEventB } else if (shouldClamp) cacheSelection(std::min(endOfString, selectionStartValue), std::min(endOfString, selectionEndValue), SelectionHasNoDirection); - setTextAsOfLastFormControlChangeEvent(normalizedValue); + setTextAsOfLastFormControlChangeEvent(String { normalizedValue }); } String HTMLTextAreaElement::defaultValue() const @@ -396,10 +396,10 @@ String HTMLTextAreaElement::validationMessage() const return validationMessageValueMissingText(); if (tooShort()) - return validationMessageTooShortText(computeLengthForSubmission(value()), minLength()); + return validationMessageTooShortText(computeLengthForSubmission(value().get()), minLength()); if (tooLong()) - return validationMessageTooLongText(computeLengthForSubmission(value()), maxLength()); + return validationMessageTooLongText(computeLengthForSubmission(value().get()), maxLength()); return String(); } @@ -424,7 +424,7 @@ bool HTMLTextAreaElement::valueMissing(StringView value) const if (!(isRequired() && isMutable())) return false; if (value.isNull()) - value = this->value(); + return this->value()->isEmpty(); return value.isEmpty(); } @@ -444,8 +444,11 @@ bool HTMLTextAreaElement::tooShort(StringView value, NeedsToCheckDirtyFlag check if (min <= 0) return false; - if (value.isNull()) - value = this->value(); + String currentValue; + if (value.isNull()) { + currentValue = this->value(); + value = currentValue; + } // The empty string is excluded from tooShort validation. if (value.isEmpty()) @@ -470,8 +473,11 @@ bool HTMLTextAreaElement::tooLong(StringView value, NeedsToCheckDirtyFlag check) if (max < 0) return false; - if (value.isNull()) - value = this->value(); + String currentValue; + if (value.isNull()) { + currentValue = this->value(); + value = currentValue; + } // FIXME: The HTML specification says that the "number of characters" is measured using code-unit length and, // in the case of textarea elements, with all line breaks normalized to a single character (as opposed to CRLF pairs). diff --git a/Source/WebCore/html/HTMLTextAreaElement.h b/Source/WebCore/html/HTMLTextAreaElement.h index f773532b29db3..6d6aaba7ca8df 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.h +++ b/Source/WebCore/html/HTMLTextAreaElement.h @@ -44,9 +44,9 @@ class HTMLTextAreaElement final : public HTMLTextFormControlElement { WEBCORE_EXPORT void setCols(unsigned); WEBCORE_EXPORT String defaultValue() const; WEBCORE_EXPORT void setDefaultValue(String&&); - WEBCORE_EXPORT String value() const final; + WEBCORE_EXPORT ValueOrReference value() const final; WEBCORE_EXPORT ExceptionOr setValue(const String&, TextFieldEventBehavior = DispatchNoEvent, TextControlSetValueSelection = TextControlSetValueSelection::SetSelectionToEnd) final; - unsigned textLength() const { return value().length(); } + unsigned textLength() const { return value()->length(); } String validationMessage() const final; void rendererWillBeDestroyed() { updateValue(); } @@ -68,7 +68,7 @@ class HTMLTextAreaElement final : public HTMLTextFormControlElement { bool supportsPlaceholder() const final { return true; } HTMLElement* placeholderElement() const final { return m_placeholder.get(); } void updatePlaceholderText() final; - bool isEmptyValue() const final { return value().isEmpty(); } + bool isEmptyValue() const final { return value()->isEmpty(); } bool isOptionalFormControl() const final { return !isRequiredFormControl(); } bool isRequiredFormControl() const final { return isRequired(); } diff --git a/Source/WebCore/html/HTMLTextFormControlElement.cpp b/Source/WebCore/html/HTMLTextFormControlElement.cpp index 05d34dbe27e32..c9d5b69c41460 100644 --- a/Source/WebCore/html/HTMLTextFormControlElement.cpp +++ b/Source/WebCore/html/HTMLTextFormControlElement.cpp @@ -112,7 +112,7 @@ Node::InsertedIntoAncestorResult HTMLTextFormControlElement::insertedIntoAncesto InsertedIntoAncestorResult InsertedIntoAncestorResult = HTMLFormControlElementWithState::insertedIntoAncestor(insertionType, parentOfInsertedTree); if (insertionType.connectedToDocument) { String initialValue = value(); - setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue); + setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? String(emptyString()) : WTFMove(initialValue)); } return InsertedIntoAncestorResult; } @@ -226,14 +226,14 @@ String HTMLTextFormControlElement::selectedText() const { if (!isTextField()) return String(); - return value().substring(selectionStart(), selectionEnd() - selectionStart()); + return value()->substring(selectionStart(), selectionEnd() - selectionStart()); } void HTMLTextFormControlElement::dispatchFormControlChangeEvent() { - if (m_textAsOfLastFormControlChangeEvent != value()) { + if (m_textAsOfLastFormControlChangeEvent != value().get()) { dispatchChangeEvent(); - setTextAsOfLastFormControlChangeEvent(value()); + setTextAsOfLastFormControlChangeEvent(String { value() }); } setChangedSinceLastFormControlChangeEvent(false); } diff --git a/Source/WebCore/html/HTMLTextFormControlElement.h b/Source/WebCore/html/HTMLTextFormControlElement.h index 7eb93fcf78816..778d8c7bc438c 100644 --- a/Source/WebCore/html/HTMLTextFormControlElement.h +++ b/Source/WebCore/html/HTMLTextFormControlElement.h @@ -26,6 +26,7 @@ #include "HTMLFormControlElementWithState.h" #include "PointerEventTypeNames.h" +#include namespace WebCore { @@ -88,7 +89,7 @@ class HTMLTextFormControlElement : public HTMLFormControlElementWithState { void dispatchFormControlChangeEvent() final; void scheduleSelectEvent(); - virtual String value() const = 0; + virtual ValueOrReference value() const = 0; virtual ExceptionOr setValue(const String&, TextFieldEventBehavior = DispatchNoEvent, TextControlSetValueSelection = TextControlSetValueSelection::SetSelectionToEnd) = 0; virtual RefPtr innerTextElement() const = 0; @@ -102,7 +103,7 @@ class HTMLTextFormControlElement : public HTMLFormControlElementWithState { String directionForFormData() const; - void setTextAsOfLastFormControlChangeEvent(const String& text) { m_textAsOfLastFormControlChangeEvent = text; } + void setTextAsOfLastFormControlChangeEvent(String&& text) { m_textAsOfLastFormControlChangeEvent = WTFMove(text); } WEBCORE_EXPORT virtual bool isInnerTextElementEditable() const; diff --git a/Source/WebCore/html/InputType.cpp b/Source/WebCore/html/InputType.cpp index ba277449d9e0b..3104e34f45c45 100644 --- a/Source/WebCore/html/InputType.cpp +++ b/Source/WebCore/html/InputType.cpp @@ -782,7 +782,7 @@ bool InputType::rendererIsNeeded() return true; } -String InputType::fallbackValue() const +ValueOrReference InputType::fallbackValue() const { return String(); } @@ -866,7 +866,7 @@ bool InputType::isEmptyValue() const return true; } -String InputType::sanitizeValue(const String& proposedValue) const +ValueOrReference InputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { return proposedValue; } diff --git a/Source/WebCore/html/InputType.h b/Source/WebCore/html/InputType.h index d6318fcb466cd..6af4ee24029c0 100644 --- a/Source/WebCore/html/InputType.h +++ b/Source/WebCore/html/InputType.h @@ -40,6 +40,7 @@ #include #include #include +#include namespace WebCore { @@ -221,7 +222,7 @@ class InputType : public RefCounted { // DOM property functions. - virtual String fallbackValue() const; // Checked last, if both internal storage and value attribute are missing. + virtual ValueOrReference fallbackValue() const; // Checked last, if both internal storage and value attribute are missing. virtual String defaultValue() const; // Checked after even fallbackValue, only when the valueWithDefault function is called. virtual WallTime valueAsDate() const; virtual ExceptionOr setValueAsDate(WallTime) const; @@ -265,7 +266,7 @@ class InputType : public RefCounted { // Return value of null string means "use the default value". // This function must be called only by HTMLInputElement::sanitizeValue(). - virtual String sanitizeValue(const String&) const; + virtual ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const; // Event handlers. diff --git a/Source/WebCore/html/MonthInputType.cpp b/Source/WebCore/html/MonthInputType.cpp index 1983c40998f06..58261843e91f5 100644 --- a/Source/WebCore/html/MonthInputType.cpp +++ b/Source/WebCore/html/MonthInputType.cpp @@ -67,7 +67,7 @@ DateComponentsType MonthInputType::dateType() const WallTime MonthInputType::valueAsDate() const { ASSERT(element()); - auto date = parseToDateComponents(element()->value()); + auto date = parseToDateComponents(element()->value().get()); if (!date) return WallTime::nan(); double msec = date->millisecondsSinceEpoch(); diff --git a/Source/WebCore/html/NumberInputType.cpp b/Source/WebCore/html/NumberInputType.cpp index 389e12fadf4e5..31821b7d9bd63 100644 --- a/Source/WebCore/html/NumberInputType.cpp +++ b/Source/WebCore/html/NumberInputType.cpp @@ -105,7 +105,7 @@ void NumberInputType::setValue(const String& sanitizedValue, bool valueChanged, double NumberInputType::valueAsDouble() const { ASSERT(element()); - return parseToDoubleForNumberType(element()->value()); + return parseToDoubleForNumberType(element()->value().get()); } ExceptionOr NumberInputType::setValueAsDouble(double newValue, TextFieldEventBehavior eventBehavior) const @@ -267,11 +267,13 @@ String NumberInputType::convertFromVisibleValue(const String& visibleValue) cons return element()->locale().convertFromLocalizedNumber(visibleValue); } -String NumberInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference NumberInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { if (proposedValue.isEmpty()) return proposedValue; - return std::isfinite(parseToDoubleForNumberType(proposedValue)) ? proposedValue : emptyString(); + if (std::isfinite(parseToDoubleForNumberType(proposedValue))) + return proposedValue; + return emptyString(); } bool NumberInputType::hasBadInput() const diff --git a/Source/WebCore/html/NumberInputType.h b/Source/WebCore/html/NumberInputType.h index 9b51a4a7595eb..6b57d3bc04fa2 100644 --- a/Source/WebCore/html/NumberInputType.h +++ b/Source/WebCore/html/NumberInputType.h @@ -60,7 +60,7 @@ class NumberInputType final : public TextFieldInputType { String localizeValue(const String&) const final; String visibleValue() const final; String convertFromVisibleValue(const String&) const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; bool hasBadInput() const final; String badInputText() const final; bool supportsPlaceholder() const final; diff --git a/Source/WebCore/html/RadioNodeList.cpp b/Source/WebCore/html/RadioNodeList.cpp index 3d49420b6005e..7512523c4683e 100644 --- a/Source/WebCore/html/RadioNodeList.cpp +++ b/Source/WebCore/html/RadioNodeList.cpp @@ -62,7 +62,7 @@ static RefPtr nonEmptyRadioButton(Element& element) return nullptr; auto& inputElement = downcast(element); - if (!inputElement.isRadioButton() || inputElement.value().isEmpty()) + if (!inputElement.isRadioButton() || inputElement.value()->isEmpty()) return nullptr; return &inputElement; } diff --git a/Source/WebCore/html/RangeInputType.cpp b/Source/WebCore/html/RangeInputType.cpp index dbedaaf385531..eb5276e904e95 100644 --- a/Source/WebCore/html/RangeInputType.cpp +++ b/Source/WebCore/html/RangeInputType.cpp @@ -96,7 +96,7 @@ const AtomString& RangeInputType::formControlType() const double RangeInputType::valueAsDouble() const { ASSERT(element()); - return parseToDoubleForNumberType(element()->value()); + return parseToDoubleForNumberType(element()->value().get()); } ExceptionOr RangeInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior) const @@ -358,19 +358,19 @@ void RangeInputType::setValue(const String& value, bool valueChanged, TextFieldE if (eventBehavior == DispatchNoEvent) { ASSERT(element()); - element()->setTextAsOfLastFormControlChangeEvent(value); + element()->setTextAsOfLastFormControlChangeEvent(String(value)); } if (hasCreatedShadowSubtree()) typedSliderThumbElement().setPositionFromValue(); } -String RangeInputType::fallbackValue() const +ValueOrReference RangeInputType::fallbackValue() const { return serializeForNumberType(createStepRange(AnyStepHandling::Reject).defaultValue()); } -String RangeInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference RangeInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { StepRange stepRange(createStepRange(AnyStepHandling::Reject)); const Decimal proposedNumericValue = parseToNumber(proposedValue, stepRange.defaultValue()); diff --git a/Source/WebCore/html/RangeInputType.h b/Source/WebCore/html/RangeInputType.h index b2b19536c4321..6b3be9c26d918 100644 --- a/Source/WebCore/html/RangeInputType.h +++ b/Source/WebCore/html/RangeInputType.h @@ -58,8 +58,8 @@ class RangeInputType final : public InputType { bool accessKeyAction(bool sendMouseEvents) final; void attributeChanged(const QualifiedName&) final; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) final; - String fallbackValue() const final; - String sanitizeValue(const String& proposedValue) const final; + ValueOrReference fallbackValue() const final; + ValueOrReference sanitizeValue(const String& proposedValue LIFETIME_BOUND) const final; bool shouldRespectListAttribute() final; HTMLElement* sliderThumbElement() const final; HTMLElement* sliderTrackElement() const final; diff --git a/Source/WebCore/html/SearchInputType.cpp b/Source/WebCore/html/SearchInputType.cpp index 241ff764de86c..9dafa8466c4aa 100644 --- a/Source/WebCore/html/SearchInputType.cpp +++ b/Source/WebCore/html/SearchInputType.cpp @@ -224,7 +224,7 @@ float SearchInputType::decorationWidth() const void SearchInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior, TextControlSetValueSelection selection) { - bool emptinessChanged = valueChanged && sanitizedValue.isEmpty() != element()->value().isEmpty(); + bool emptinessChanged = valueChanged && sanitizedValue.isEmpty() != element()->value()->isEmpty(); BaseTextInputType::setValue(sanitizedValue, valueChanged, eventBehavior, selection); diff --git a/Source/WebCore/html/TextFieldInputType.cpp b/Source/WebCore/html/TextFieldInputType.cpp index fa584252f7fc6..b9ce94e5e5f0b 100644 --- a/Source/WebCore/html/TextFieldInputType.cpp +++ b/Source/WebCore/html/TextFieldInputType.cpp @@ -178,7 +178,7 @@ void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChange // FIXME: Why do we do this when eventBehavior == DispatchNoEvent if (!input->focused() || eventBehavior == DispatchNoEvent) - input->setTextAsOfLastFormControlChangeEvent(sanitizedValue); + input->setTextAsOfLastFormControlChangeEvent(String { sanitizedValue }); if (UserTypingGestureIndicator::processingUserTypingGesture()) didSetValueByUserEdit(); @@ -507,7 +507,7 @@ bool TextFieldInputType::shouldOnlyShowDataListDropdownButtonWhenFocusedOrEdited #endif // ENABLE(DATALIST_ELEMENT) -static String limitLength(const String& string, unsigned maxLength) +static ValueOrReference limitLength(const String& string LIFETIME_BOUND, unsigned maxLength) { unsigned newLength = std::min(maxLength, string.length()); if (newLength == string.length()) @@ -592,9 +592,13 @@ static bool isAutoFillButtonTypeChanged(const AtomString& attribute, AutoFillBut return false; } -String TextFieldInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference TextFieldInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { - return limitLength(proposedValue.removeCharacters(isHTMLLineBreak), HTMLInputElement::maxEffectiveLength); + if (LIKELY(proposedValue.find(isHTMLLineBreak) == notFound)) + return limitLength(proposedValue, HTMLInputElement::maxEffectiveLength); + + auto proposedValueWithoutLineBreaks = proposedValue.removeCharacters(isHTMLLineBreak); + return String { limitLength(proposedValueWithoutLineBreaks, HTMLInputElement::maxEffectiveLength) }; } void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent& event) diff --git a/Source/WebCore/html/TextFieldInputType.h b/Source/WebCore/html/TextFieldInputType.h index 1bac905c93240..06392d8715fb1 100644 --- a/Source/WebCore/html/TextFieldInputType.h +++ b/Source/WebCore/html/TextFieldInputType.h @@ -82,7 +82,7 @@ class TextFieldInputType : public InputType, protected SpinButtonElement::SpinBu void handleBlurEvent() final; void setValue(const String&, bool valueChanged, TextFieldEventBehavior, TextControlSetValueSelection) override; void updateInnerTextValue() final; - String sanitizeValue(const String&) const override; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const override; virtual String convertFromVisibleValue(const String&) const; virtual void didSetValueByUserEdit(); diff --git a/Source/WebCore/html/URLInputType.cpp b/Source/WebCore/html/URLInputType.cpp index 060a4f58c1736..a899502b85846 100644 --- a/Source/WebCore/html/URLInputType.cpp +++ b/Source/WebCore/html/URLInputType.cpp @@ -61,7 +61,7 @@ String URLInputType::typeMismatchText() const return validationMessageTypeMismatchForURLText(); } -String URLInputType::sanitizeValue(const String& proposedValue) const +ValueOrReference URLInputType::sanitizeValue(const String& proposedValue LIFETIME_BOUND) const { return stripLeadingAndTrailingHTMLSpaces(BaseTextInputType::sanitizeValue(proposedValue)); } diff --git a/Source/WebCore/html/URLInputType.h b/Source/WebCore/html/URLInputType.h index fa8b2ee1a8b8f..3b7ab8ef95a1b 100644 --- a/Source/WebCore/html/URLInputType.h +++ b/Source/WebCore/html/URLInputType.h @@ -48,7 +48,7 @@ class URLInputType final : public BaseTextInputType { const AtomString& formControlType() const final; bool typeMismatch() const final; String typeMismatchText() const final; - String sanitizeValue(const String&) const final; + ValueOrReference sanitizeValue(const String& value LIFETIME_BOUND) const final; }; } // namespace WebCore diff --git a/Source/WebCore/html/parser/AtomHTMLToken.h b/Source/WebCore/html/parser/AtomHTMLToken.h index 125e605a4e99d..e19d590344009 100644 --- a/Source/WebCore/html/parser/AtomHTMLToken.h +++ b/Source/WebCore/html/parser/AtomHTMLToken.h @@ -203,20 +203,21 @@ inline void AtomHTMLToken::initializeAttributes(const HTMLToken::AttributeList& if (!size) return; - HashSet addedAttributes; + Vector addedAttributes; addedAttributes.reserveInitialCapacity(size); - m_attributes.reserveInitialCapacity(size); - for (auto& attribute : attributes) { + + m_attributes = WTF::compactMap(attributes, [&](auto& attribute) -> std::optional { if (attribute.name.isEmpty()) - continue; + return std::nullopt; auto qualifiedName = HTMLNameCache::makeAttributeQualifiedName(attribute.name); - - if (addedAttributes.add(qualifiedName.localName()).isNewEntry) - m_attributes.uncheckedAppend(Attribute(WTFMove(qualifiedName), HTMLNameCache::makeAttributeValue(attribute.value))); - else + if (UNLIKELY(!insertInUniquedSortedVector(addedAttributes, qualifiedName.localName().impl()))) { m_hasDuplicateAttribute = true; - } + return std::nullopt; + } + + return Attribute(WTFMove(qualifiedName), HTMLNameCache::makeAttributeValue(attribute.value)); + }); } inline AtomHTMLToken::AtomHTMLToken(HTMLToken& token) diff --git a/Source/WebCore/html/shadow/DateTimeFieldElement.cpp b/Source/WebCore/html/shadow/DateTimeFieldElement.cpp index 03c82c55d6da0..41c3c6e2ac2de 100644 --- a/Source/WebCore/html/shadow/DateTimeFieldElement.cpp +++ b/Source/WebCore/html/shadow/DateTimeFieldElement.cpp @@ -182,7 +182,9 @@ AtomString DateTimeFieldElement::localeIdentifier() const String DateTimeFieldElement::visibleValue() const { - return hasValue() ? value() : placeholderValue(); + if (hasValue()) + return value(); + return placeholderValue(); } void DateTimeFieldElement::updateVisibleValue(EventBehavior eventBehavior) diff --git a/Source/WebCore/html/shadow/DateTimeFieldElement.h b/Source/WebCore/html/shadow/DateTimeFieldElement.h index 3dc54613636b4..8e9f739d5d04f 100644 --- a/Source/WebCore/html/shadow/DateTimeFieldElement.h +++ b/Source/WebCore/html/shadow/DateTimeFieldElement.h @@ -31,6 +31,7 @@ #include "HTMLDivElement.h" #include +#include #include namespace WebCore { @@ -70,7 +71,7 @@ class DateTimeFieldElement : public HTMLDivElement { virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) = 0; virtual void stepDown() = 0; virtual void stepUp() = 0; - virtual String value() const = 0; + virtual ValueOrReference value() const = 0; virtual String placeholderValue() const = 0; protected: diff --git a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp index a31e9246f8150..2268373a269bb 100644 --- a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp +++ b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.cpp @@ -145,9 +145,11 @@ void DateTimeNumericFieldElement::stepUp() setValueAsIntegerByStepping(newValue); } -String DateTimeNumericFieldElement::value() const +ValueOrReference DateTimeNumericFieldElement::value() const { - return m_hasValue ? formatValue(m_value) : emptyString(); + if (m_hasValue) + return formatValue(m_value); + return emptyString(); } String DateTimeNumericFieldElement::placeholderValue() const diff --git a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h index ba636b5a38828..644329d3d00f8 100644 --- a/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h +++ b/Source/WebCore/html/shadow/DateTimeNumericFieldElement.h @@ -64,7 +64,7 @@ class DateTimeNumericFieldElement : public DateTimeFieldElement { private: // DateTimeFieldElement functions: void adjustMinWidth(RenderStyle&) const final; - String value() const final; + ValueOrReference value() const final; String placeholderValue() const final; void handleKeyboardEvent(KeyboardEvent&) final; void handleBlurEvent(Event&) final; diff --git a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp index 93312a496d2a2..8acee46f02324 100644 --- a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp +++ b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.cpp @@ -100,9 +100,11 @@ void DateTimeSymbolicFieldElement::stepUp() setValueAsInteger(newValue, DispatchInputAndChangeEvents); } -String DateTimeSymbolicFieldElement::value() const +ValueOrReference DateTimeSymbolicFieldElement::value() const { - return hasValue() ? m_symbols[m_selectedIndex] : emptyString(); + if (hasValue()) + return m_symbols[m_selectedIndex]; + return emptyString(); } String DateTimeSymbolicFieldElement::placeholderValue() const diff --git a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h index a43611dc79833..0badaf68fe629 100644 --- a/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h +++ b/Source/WebCore/html/shadow/DateTimeSymbolicFieldElement.h @@ -51,7 +51,7 @@ class DateTimeSymbolicFieldElement : public DateTimeFieldElement, public TypeAhe void adjustMinWidth(RenderStyle&) const final; void stepDown() final; void stepUp() final; - String value() const final; + ValueOrReference value() const final; String placeholderValue() const final; void handleKeyboardEvent(KeyboardEvent&) final; diff --git a/Source/WebCore/html/shadow/SliderThumbElement.cpp b/Source/WebCore/html/shadow/SliderThumbElement.cpp index 96e1af721a7e2..5313997a4156b 100644 --- a/Source/WebCore/html/shadow/SliderThumbElement.cpp +++ b/Source/WebCore/html/shadow/SliderThumbElement.cpp @@ -68,7 +68,7 @@ WTF_MAKE_ISO_ALLOCATED_IMPL(SliderContainerElement); inline static Decimal sliderPosition(HTMLInputElement& element) { const StepRange stepRange(element.createStepRange(AnyStepHandling::Reject)); - const Decimal oldValue = parseToDecimalForNumberType(element.value(), stepRange.defaultValue()); + const Decimal oldValue = parseToDecimalForNumberType(element.value().get(), stepRange.defaultValue()); return stepRange.proportionFromValue(stepRange.clampValue(oldValue)); } diff --git a/Source/WebCore/html/shadow/TextControlInnerElements.cpp b/Source/WebCore/html/shadow/TextControlInnerElements.cpp index 2728fa63b4ae4..cb97f60c55034 100644 --- a/Source/WebCore/html/shadow/TextControlInnerElements.cpp +++ b/Source/WebCore/html/shadow/TextControlInnerElements.cpp @@ -314,7 +314,7 @@ std::optional SearchFieldCancelButtonElement::resolveCustom { auto elementStyle = resolveStyle(resolutionContext); auto& inputElement = downcast(*shadowHost()); - elementStyle.renderStyle->setVisibility(elementStyle.renderStyle->visibility() == Visibility::Hidden || inputElement.value().isEmpty() ? Visibility::Hidden : Visibility::Visible); + elementStyle.renderStyle->setVisibility(elementStyle.renderStyle->visibility() == Visibility::Hidden || inputElement.value()->isEmpty() ? Visibility::Hidden : Visibility::Visible); return elementStyle; } diff --git a/Source/WebCore/rendering/RenderSearchField.cpp b/Source/WebCore/rendering/RenderSearchField.cpp index 48d49947a9d65..6d915dcbb76be 100644 --- a/Source/WebCore/rendering/RenderSearchField.cpp +++ b/Source/WebCore/rendering/RenderSearchField.cpp @@ -197,7 +197,7 @@ void RenderSearchField::updateCancelButtonVisibility() const Visibility RenderSearchField::visibilityForCancelButton() const { - return (style().visibility() == Visibility::Hidden || inputElement().value().isEmpty()) ? Visibility::Hidden : Visibility::Visible; + return (style().visibility() == Visibility::Hidden || inputElement().value()->isEmpty()) ? Visibility::Hidden : Visibility::Visible; } const AtomString& RenderSearchField::autosaveName() const diff --git a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm index ebafab8274c8c..34cfc6572812c 100644 --- a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm +++ b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm @@ -4619,7 +4619,7 @@ static VisiblePositionRange constrainRangeToSelection(const VisiblePositionRange } else if (is(element)) { auto& textFormControlElement = downcast(*element); rangeOfInterest.start = textFormControlElement.visiblePositionForIndex(0); - rangeOfInterest.end = textFormControlElement.visiblePositionForIndex(textFormControlElement.value().length()); + rangeOfInterest.end = textFormControlElement.visiblePositionForIndex(textFormControlElement.value()->length()); } else { rangeOfInterest.start = firstPositionInOrBeforeNode(element.get()); rangeOfInterest.end = lastPositionInOrAfterNode(element.get()); diff --git a/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm b/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm index 23c7d339659ce..7230f09d11681 100644 --- a/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm +++ b/Source/WebKitLegacy/ios/WebCoreSupport/WebVisiblePosition.mm @@ -534,7 +534,7 @@ - (WebVisiblePosition *)endPosition return [super endPosition]; RenderTextControl& textControl = downcast(*object); - VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value().length()); + VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value()->length()); return [WebVisiblePosition _wrapVisiblePosition:visiblePosition]; } @@ -561,7 +561,7 @@ - (WebVisiblePosition *)endPosition return [super endPosition]; RenderTextControl& textControl = downcast(*object); - VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value().length()); + VisiblePosition visiblePosition = textControl.textFormControlElement().visiblePositionForIndex(textControl.textFormControlElement().value()->length()); return [WebVisiblePosition _wrapVisiblePosition:visiblePosition]; } diff --git a/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm b/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm index 9a29a5f354992..86bfd58d80be8 100644 --- a/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm +++ b/Source/WebKitLegacy/mac/DOM/DOMHTMLInputElement.mm @@ -451,7 +451,7 @@ - (void)setDefaultValue:(NSString *)newDefaultValue - (NSString *)value { WebCore::JSMainThreadNullState state; - return IMPL->value(); + return IMPL->value().get(); } - (void)setValue:(NSString *)newValue diff --git a/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm b/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm index cd3dacf371033..b1689a613dfc4 100644 --- a/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm +++ b/Source/WebKitLegacy/mac/DOM/DOMHTMLTextAreaElement.mm @@ -212,7 +212,7 @@ - (void)setDefaultValue:(NSString *)newDefaultValue - (NSString *)value { WebCore::JSMainThreadNullState state; - return unwrap(*self).value(); + return unwrap(*self).value().get(); } - (void)setValue:(NSString *)newValue