From afa2ed9e70103469f02732323fa7b874378d6a5d Mon Sep 17 00:00:00 2001 From: Geoffrey Garen Date: Thu, 17 Apr 2025 13:42:10 -0700 Subject: [PATCH] StringImpl refcounting should be atomic https://bugs.webkit.org/show_bug.cgi?id=289962 rdar://147313174 Backport notes: I removed some optimizations; ref churn is not great, but still correct. Original notes: Reviewed by Antti Koivisto, Yusuke Suzuki, Chris Dumez, and Keith Miller. This patch takes the first step to making WTF::String thread-safe: Making m_refcount increment and decrement atomic. (Hash, flags, and the AtomString table are not yet atomic. I plan to address those in my next patch.) The direct code change is pretty small, but it measured as a performance regression. So most of this code is optimization. I got rid of about 1/3 of WTF::StringImpl refcounts on Speedometer and about 1/2 on JetStream. ARM has a special ldadd instruction for fast atomics. Net net, it measures as about 1.5X slower than just add. That's pretty good! But it still required some optimizations to be affordable. Intel atomics are much slower, so it took far more optimizations to get Intel across the finish line. A/B testers suggest that we might still have a < 0.5% JetStream regression on Intel. I checked in with Yusuke, Keith, and Maciej, and we can move forward because we have a large total JetStream speedup on Intel this year, so customers on older hardware will still see a net speedup when they get new software. --- Source/WTF/WTF.xcodeproj/project.pbxproj | 4 + Source/WTF/wtf/CMakeLists.txt | 1 + Source/WTF/wtf/Compiler.h | 10 +++ Source/WTF/wtf/ValueOrReference.h | 78 +++++++++++++++++++ Source/WTF/wtf/Vector.h | 11 +++ Source/WTF/wtf/text/StringImpl.cpp | 1 + Source/WTF/wtf/text/StringImpl.h | 22 +++--- .../AccessibilityRenderObject.cpp | 6 +- .../accessibility/AccessibilitySlider.cpp | 2 +- .../ios/AccessibilityObjectIOS.mm | 2 +- Source/WebCore/dom/Attribute.h | 6 ++ .../editing/cocoa/AutofillElements.cpp | 4 +- Source/WebCore/editing/cocoa/HTMLConverter.mm | 4 +- .../WebCore/html/BaseCheckableInputType.cpp | 4 +- Source/WebCore/html/BaseCheckableInputType.h | 2 +- .../WebCore/html/BaseDateAndTimeInputType.cpp | 10 ++- .../WebCore/html/BaseDateAndTimeInputType.h | 2 +- Source/WebCore/html/ColorInputType.cpp | 8 +- Source/WebCore/html/ColorInputType.h | 4 +- .../WebCore/html/DateTimeLocalInputType.cpp | 8 +- Source/WebCore/html/DateTimeLocalInputType.h | 2 +- Source/WebCore/html/EmailInputType.cpp | 2 +- Source/WebCore/html/EmailInputType.h | 2 +- Source/WebCore/html/HTMLElement.cpp | 2 +- Source/WebCore/html/HTMLInputElement.cpp | 24 +++--- Source/WebCore/html/HTMLInputElement.h | 5 +- Source/WebCore/html/HTMLTextAreaElement.cpp | 28 ++++--- Source/WebCore/html/HTMLTextAreaElement.h | 6 +- .../html/HTMLTextFormControlElement.cpp | 8 +- .../WebCore/html/HTMLTextFormControlElement.h | 5 +- Source/WebCore/html/InputType.cpp | 4 +- Source/WebCore/html/InputType.h | 5 +- Source/WebCore/html/MonthInputType.cpp | 2 +- Source/WebCore/html/NumberInputType.cpp | 8 +- Source/WebCore/html/NumberInputType.h | 2 +- Source/WebCore/html/RadioNodeList.cpp | 2 +- Source/WebCore/html/RangeInputType.cpp | 8 +- Source/WebCore/html/RangeInputType.h | 4 +- Source/WebCore/html/SearchInputType.cpp | 2 +- Source/WebCore/html/TextFieldInputType.cpp | 12 ++- Source/WebCore/html/TextFieldInputType.h | 2 +- Source/WebCore/html/URLInputType.cpp | 2 +- Source/WebCore/html/URLInputType.h | 2 +- Source/WebCore/html/parser/AtomHTMLToken.h | 19 ++--- .../html/shadow/DateTimeFieldElement.cpp | 4 +- .../html/shadow/DateTimeFieldElement.h | 3 +- .../shadow/DateTimeNumericFieldElement.cpp | 6 +- .../html/shadow/DateTimeNumericFieldElement.h | 2 +- .../shadow/DateTimeSymbolicFieldElement.cpp | 6 +- .../shadow/DateTimeSymbolicFieldElement.h | 2 +- .../html/shadow/SliderThumbElement.cpp | 2 +- .../html/shadow/TextControlInnerElements.cpp | 2 +- .../WebCore/rendering/RenderSearchField.cpp | 2 +- .../WebProcess/WebPage/ios/WebPageIOS.mm | 2 +- .../ios/WebCoreSupport/WebVisiblePosition.mm | 4 +- .../mac/DOM/DOMHTMLInputElement.mm | 2 +- .../mac/DOM/DOMHTMLTextAreaElement.mm | 2 +- 57 files changed, 262 insertions(+), 124 deletions(-) create mode 100644 Source/WTF/wtf/ValueOrReference.h 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