Title: [281505] trunk/Source/WebCore
Revision
281505
Author
[email protected]
Date
2021-08-24 11:32:59 -0700 (Tue, 24 Aug 2021)

Log Message

Remove inefficient use of HashMap from FormController along with a bit of other streamlining and modernizing
https://bugs.webkit.org/show_bug.cgi?id=228854

Reviewed by Chris Dumez.

* html/FormController.cpp:
(WebCore::ownerForm): Renamed from ownerFormForState. Removed unnecessary use of
inline; trust the compiler to inline if needed without the keyword. Removed
"using namespace HTMLNames". Use nullptr instead of 0.
(WebCore::StringVectorReader::consumeString): Added. Helps parse a vector without
so many index range checks. Returns null string at end of vector.
(WebCore::StringVectorReader::consumeSubvector): Added. Helps parse a vector
without so many index range checks. Returns the subvector.
(WebCore::appendSerializedFormControlState): Renamed from
serializeFormControlStateTo to make clear this appends to an existing vector.
(WebCore::consumeSerializedFormControlState): Renamed from
deserializeFormControlState to make clear this reads part of a vector and
indicates what was read. Use the StringVectorReader class for the argument type
to make that clear. Refactored to use the new consumeString and consumeSubvector
functions. Also use parseInteger because there is no need to allow trailing junk.
(WebCore::FormElementKey): Deleted this entire class, kind of a lot of code,
because we can instead use std::pair and only lose a little bit of clarity.
(WebCore::FormController::SavedFormState): Made this class a member of
FormController. Also use std::pair, renamed m_stateForNewFormElements to m_map,
and removed m_controlStateCount. The only code that was using this was
in SavedFormState::serializeTo, and that in turn was only used on a
SavedFormState which contained
(WebCore::SavedFormState::serializeTo const): Deleted. All the logic from this
function is now in the FormController::formElementsState function, which is
the only place it was used.
(WebCore::FormController::SavedFormState::consumeSerializedState): Renamed
from deserialize to make it clear that it reads part of a vector and indicates
what is read. Use the StringVectorReader class for the argument type to make
that clear. Moved the isNotFormControlTypeCharacter from a separate function
to be a lambda inside this function. Use consumeString, StringView::contains,
and consumeSerializedFormControlState to streamline the implementation.
Moved call to HashMap::add here instead of separate appendControlState
function since we no longer need to track m_controlStateCount.
(WebCore::SavedFormState::appendControlState): Deleted.
(WebCore::FormController::SavedFormState::takeControlState): Take the arguments
as a FormElementKey, already in a pair, instead of as two separate values.
Removed the code to maintain m_controlStateCount.
(WebCore::FormController::SavedFormState::appendReferencedFilePaths const):
Renamed from referencedFilePaths and change this to append to an existing
vector, since that's what the only caller was doing.
(WebCore::FormController::FormKeyGenerator): Made this class a member of
FormController. Also changed to just use String for the formKey, since there
is no significant sharing or other optimization made possible by using
AtomString. Also use a reference instead of a pointer in one place.
(WebCore::recordFormStructure): Deleted.
(WebCore::formSignature): Merge in the logic from the recordFormStructure
function, since that was an awkward function name and this is clearer as
just one function. Added a comment about fragment identifiers. Use unsigned
instead of size_t to count up to 2. Improved the comment about why the
maximum is 2 and used simpler loop logic, is<> instead of a specific
function call, downcast<> instead of static_cast<>, and Ref instead of RefPtr<...>.
(WebCore::FormController::FormKeyGenerator::formKey): Use RefPtr on the
left side instead of makeRefPtr. Use String instead of AtomString.
(WebCore::FormController::FormKeyGenerator::willDeleteForm): Take a reference.
(WebCore::formStateSignature): Use MainThreadNeverDestroyed.
(WebCore::FormController::createSavedFormStateMap): Deleted. This was only
ever used by formElementsState, which does not need a map.
(WebCore::FormController::formElementsState const): Use a vector that
uses Ref<> since it's a little safer for object lifetime and that also stores
the form key so we can grouop controls in the same form as part of sorting.
Added an early return when there are no controls instead of
detecting that at the end of the function. Moved the code from
createSavedFormStateMap and SavedFormState::serializeTo in here to avoid
creating a HashMap just to iterate it and build a string vector, using
sorting to group and count the controls in each form. Use shrinkToFit to make
sure the vector is not permanently overallocated instead of an always-too-small
reserveInitialCapacity.
(WebCore::FormController::setStateForNewFormElements): Update for the change in
parseStateVector.
(WebCore::FormController::takeStateForFormElement): Updated for the change to
m_savedFormStateMap values. Use auto a bit and "iterator" insted of "it".
(WebCore::FormController::parseStateVector): Renamed from
formStatesFromStateVector and changed to use a return value instead of an out
argument. Use StringVectorReader and consumeString to streamline the code and
make it a bit more foolproof. Check the exhaustion of the input vector inside
the loop instead of checking for an error after the loop.
(WebCore::FormController::willDeleteForm): Pass a reference.
(WebCore::FormController::restoreControlStateFor): Tweak comment and use ||
instead of two separate if statements.
(WebCore::FormController::restoreControlStateIn): Ditto.
(WebCore::FormController::referencedFilePaths): Use parseStateVector and
appendReferencedFilePaths.

* html/FormController.h: Removed almost all the includes, which were not needed.
Made the FormKeyGenerator and SavedFormState classes members of FormController.
Changed SavedFormStateMap to use String as the key instead of RefPtr<AtomStringImpl>,
no need for the exotic type. Changed SavedFormStateMap to use SavedFormState
as the value instead of std::unique_ptr<SavedFormState>. Removed the unused
member function createSavedFormStateMap and renamed formStatesFromStateVector
to parseStateVector and changed it to use a return value.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (281504 => 281505)


--- trunk/Source/WebCore/ChangeLog	2021-08-24 18:31:09 UTC (rev 281504)
+++ trunk/Source/WebCore/ChangeLog	2021-08-24 18:32:59 UTC (rev 281505)
@@ -1,3 +1,101 @@
+2021-08-24  Darin Adler  <[email protected]>
+
+        Remove inefficient use of HashMap from FormController along with a bit of other streamlining and modernizing
+        https://bugs.webkit.org/show_bug.cgi?id=228854
+
+        Reviewed by Chris Dumez.
+
+        * html/FormController.cpp:
+        (WebCore::ownerForm): Renamed from ownerFormForState. Removed unnecessary use of
+        inline; trust the compiler to inline if needed without the keyword. Removed
+        "using namespace HTMLNames". Use nullptr instead of 0.
+        (WebCore::StringVectorReader::consumeString): Added. Helps parse a vector without
+        so many index range checks. Returns null string at end of vector.
+        (WebCore::StringVectorReader::consumeSubvector): Added. Helps parse a vector
+        without so many index range checks. Returns the subvector.
+        (WebCore::appendSerializedFormControlState): Renamed from
+        serializeFormControlStateTo to make clear this appends to an existing vector.
+        (WebCore::consumeSerializedFormControlState): Renamed from
+        deserializeFormControlState to make clear this reads part of a vector and
+        indicates what was read. Use the StringVectorReader class for the argument type
+        to make that clear. Refactored to use the new consumeString and consumeSubvector
+        functions. Also use parseInteger because there is no need to allow trailing junk.
+        (WebCore::FormElementKey): Deleted this entire class, kind of a lot of code,
+        because we can instead use std::pair and only lose a little bit of clarity.
+        (WebCore::FormController::SavedFormState): Made this class a member of
+        FormController. Also use std::pair, renamed m_stateForNewFormElements to m_map,
+        and removed m_controlStateCount. The only code that was using this was
+        in SavedFormState::serializeTo, and that in turn was only used on a
+        SavedFormState which contained
+        (WebCore::SavedFormState::serializeTo const): Deleted. All the logic from this
+        function is now in the FormController::formElementsState function, which is
+        the only place it was used.
+        (WebCore::FormController::SavedFormState::consumeSerializedState): Renamed
+        from deserialize to make it clear that it reads part of a vector and indicates
+        what is read. Use the StringVectorReader class for the argument type to make
+        that clear. Moved the isNotFormControlTypeCharacter from a separate function
+        to be a lambda inside this function. Use consumeString, StringView::contains,
+        and consumeSerializedFormControlState to streamline the implementation.
+        Moved call to HashMap::add here instead of separate appendControlState
+        function since we no longer need to track m_controlStateCount.
+        (WebCore::SavedFormState::appendControlState): Deleted.
+        (WebCore::FormController::SavedFormState::takeControlState): Take the arguments
+        as a FormElementKey, already in a pair, instead of as two separate values.
+        Removed the code to maintain m_controlStateCount.
+        (WebCore::FormController::SavedFormState::appendReferencedFilePaths const):
+        Renamed from referencedFilePaths and change this to append to an existing
+        vector, since that's what the only caller was doing.
+        (WebCore::FormController::FormKeyGenerator): Made this class a member of
+        FormController. Also changed to just use String for the formKey, since there
+        is no significant sharing or other optimization made possible by using
+        AtomString. Also use a reference instead of a pointer in one place.
+        (WebCore::recordFormStructure): Deleted.
+        (WebCore::formSignature): Merge in the logic from the recordFormStructure
+        function, since that was an awkward function name and this is clearer as
+        just one function. Added a comment about fragment identifiers. Use unsigned
+        instead of size_t to count up to 2. Improved the comment about why the
+        maximum is 2 and used simpler loop logic, is<> instead of a specific
+        function call, downcast<> instead of static_cast<>, and Ref instead of RefPtr<...>.
+        (WebCore::FormController::FormKeyGenerator::formKey): Use RefPtr on the
+        left side instead of makeRefPtr. Use String instead of AtomString.
+        (WebCore::FormController::FormKeyGenerator::willDeleteForm): Take a reference.
+        (WebCore::formStateSignature): Use MainThreadNeverDestroyed.
+        (WebCore::FormController::createSavedFormStateMap): Deleted. This was only
+        ever used by formElementsState, which does not need a map.
+        (WebCore::FormController::formElementsState const): Use a vector that
+        uses Ref<> since it's a little safer for object lifetime and that also stores
+        the form key so we can grouop controls in the same form as part of sorting.
+        Added an early return when there are no controls instead of
+        detecting that at the end of the function. Moved the code from
+        createSavedFormStateMap and SavedFormState::serializeTo in here to avoid
+        creating a HashMap just to iterate it and build a string vector, using
+        sorting to group and count the controls in each form. Use shrinkToFit to make
+        sure the vector is not permanently overallocated instead of an always-too-small
+        reserveInitialCapacity.
+        (WebCore::FormController::setStateForNewFormElements): Update for the change in
+        parseStateVector.
+        (WebCore::FormController::takeStateForFormElement): Updated for the change to
+        m_savedFormStateMap values. Use auto a bit and "iterator" insted of "it".
+        (WebCore::FormController::parseStateVector): Renamed from
+        formStatesFromStateVector and changed to use a return value instead of an out
+        argument. Use StringVectorReader and consumeString to streamline the code and
+        make it a bit more foolproof. Check the exhaustion of the input vector inside
+        the loop instead of checking for an error after the loop.
+        (WebCore::FormController::willDeleteForm): Pass a reference.
+        (WebCore::FormController::restoreControlStateFor): Tweak comment and use ||
+        instead of two separate if statements.
+        (WebCore::FormController::restoreControlStateIn): Ditto.
+        (WebCore::FormController::referencedFilePaths): Use parseStateVector and
+        appendReferencedFilePaths.
+
+        * html/FormController.h: Removed almost all the includes, which were not needed.
+        Made the FormKeyGenerator and SavedFormState classes members of FormController.
+        Changed SavedFormStateMap to use String as the key instead of RefPtr<AtomStringImpl>,
+        no need for the exotic type. Changed SavedFormStateMap to use SavedFormState
+        as the value instead of std::unique_ptr<SavedFormState>. Removed the unused
+        member function createSavedFormStateMap and renamed formStatesFromStateVector
+        to parseStateVector and changed it to use a return value.
+
 2021-08-24  Jer Noble  <[email protected]>
 
         Add support for new pseudo-classes for media from CSS Selectors Level 4.

Modified: trunk/Source/WebCore/html/FormController.cpp (281504 => 281505)


--- trunk/Source/WebCore/html/FormController.cpp	2021-08-24 18:31:09 UTC (rev 281504)
+++ trunk/Source/WebCore/html/FormController.cpp	2021-08-24 18:32:59 UTC (rev 281505)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2021 Apple Inc. All rights reserved.
  * Copyright (C) 2010, 2011, 2012 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -33,19 +33,41 @@
 
 namespace WebCore {
 
-using namespace HTMLNames;
-
-static inline HTMLFormElement* ownerFormForState(const HTMLFormControlElementWithState& control)
+static HTMLFormElement* ownerForm(const HTMLFormControlElementWithState& control)
 {
     // Assume controls with form attribute have no owners because we restore
     // state during parsing and form owners of such controls might be
     // indeterminate.
-    return control.hasAttributeWithoutSynchronization(formAttr) ? 0 : control.form();
+    return control.hasAttributeWithoutSynchronization(HTMLNames::formAttr) ? nullptr : control.form();
 }
 
+struct StringVectorReader {
+    const Vector<String>& vector;
+    size_t index { 0 };
+
+    const String& consumeString();
+    Vector<String> consumeSubvector(size_t subvectorSize);
+};
+
+const String& StringVectorReader::consumeString()
+{
+    if (index == vector.size())
+        return nullString();
+    return vector[index++];
+}
+
+Vector<String> StringVectorReader::consumeSubvector(size_t subvectorSize)
+{
+    if (subvectorSize > vector.size() - index)
+        return { };
+    auto subvectorIndex = index;
+    index += subvectorSize;
+    return { vector.data() + subvectorIndex, subvectorSize };
+}
+
 // ----------------------------------------------------------------------------
 
-// Serilized form of FormControlState:
+// Serialized form of FormControlState:
 //  (',' means strings around it are separated in stateVector.)
 //
 // SerializedControlState ::= SkipState | RestoreState
@@ -54,287 +76,155 @@
 // UnsignedNumber ::= [0-9]+
 // ControlValue ::= arbitrary string
 //
-// RestoreState has a sequence of ControlValues. The length of the
-// sequence is represented by UnsignedNumber.
+// The UnsignedNumber in RestoreState is the length of the sequence of ControlValues.
 
-static inline void serializeFormControlStateTo(const FormControlState& formControlState, Vector<String>& stateVector)
+static void appendSerializedFormControlState(Vector<String>& vector, const FormControlState& state)
 {
-    stateVector.append(String::number(formControlState.size()));
-    for (auto& value : formControlState)
-        stateVector.append(value.isNull() ? emptyString() : value);
+    vector.append(String::number(state.size()));
+    for (auto& value : state)
+        vector.append(value.isNull() ? emptyString() : value);
 }
 
-static inline std::optional<FormControlState> deserializeFormControlState(const Vector<String>& stateVector, size_t& index)
+static std::optional<FormControlState> consumeSerializedFormControlState(StringVectorReader& reader)
 {
-    if (index >= stateVector.size())
+    auto sizeString = reader.consumeString();
+    if (sizeString.isNull())
         return std::nullopt;
-    auto size = parseIntegerAllowingTrailingJunk<size_t>(stateVector[index++]).value_or(0);
-    if (index + size > stateVector.size())
-        return std::nullopt;
-    Vector<String> subvector;
-    subvector.reserveInitialCapacity(size);
-    for (size_t i = 0; i < size; ++i)
-        subvector.uncheckedAppend(stateVector[index++]);
-    return subvector;
+    return reader.consumeSubvector(parseInteger<size_t>(sizeString).value_or(0));
 }
 
 // ----------------------------------------------------------------------------
 
-class FormElementKey {
+// ----------------------------------------------------------------------------
+
+class FormController::SavedFormState {
 public:
-    explicit FormElementKey(AtomStringImpl* = nullptr, AtomStringImpl* = nullptr);
-    ~FormElementKey();
-    FormElementKey(const FormElementKey&);
-    FormElementKey& operator=(const FormElementKey&);
+    static SavedFormState consumeSerializedState(StringVectorReader&);
 
-    AtomStringImpl* name() const { return m_name; }
-    AtomStringImpl* type() const { return m_type; }
+    bool isEmpty() const { return m_map.isEmpty(); }
 
-    // Hash table deleted values, which are only constructed and never copied or destroyed.
-    FormElementKey(WTF::HashTableDeletedValueType) : m_name(hashTableDeletedValue()) { }
-    bool isHashTableDeletedValue() const { return m_name == hashTableDeletedValue(); }
+    using FormElementKey = std::pair<AtomString, AtomString>;
+    FormControlState takeControlState(const FormElementKey&);
 
-private:
-    void ref() const;
-    void deref() const;
+    void appendReferencedFilePaths(Vector<String>&) const;
 
-    static AtomStringImpl* hashTableDeletedValue() { return reinterpret_cast<AtomStringImpl*>(-1); }
-
-    AtomStringImpl* m_name;
-    AtomStringImpl* m_type;
-};
-
-FormElementKey::FormElementKey(AtomStringImpl* name, AtomStringImpl* type)
-    : m_name(name)
-    , m_type(type)
-{
-    ref();
-}
-
-FormElementKey::~FormElementKey()
-{
-    deref();
-}
-
-FormElementKey::FormElementKey(const FormElementKey& other)
-    : m_name(other.name())
-    , m_type(other.type())
-{
-    ref();
-}
-
-FormElementKey& FormElementKey::operator=(const FormElementKey& other)
-{
-    other.ref();
-    deref();
-    m_name = other.name();
-    m_type = other.type();
-    return *this;
-}
-
-void FormElementKey::ref() const
-{
-    if (name())
-        name()->ref();
-    if (type())
-        type()->ref();
-}
-
-void FormElementKey::deref() const
-{
-    if (name())
-        name()->deref();
-    if (type())
-        type()->deref();
-}
-
-inline bool operator==(const FormElementKey& a, const FormElementKey& b)
-{
-    return a.name() == b.name() && a.type() == b.type();
-}
-
-struct FormElementKeyHash {
-    static unsigned hash(const FormElementKey&);
-    static bool equal(const FormElementKey& a, const FormElementKey& b) { return a == b; }
-    static const bool safeToCompareToEmptyOrDeleted = true;
-};
-
-unsigned FormElementKeyHash::hash(const FormElementKey& key)
-{
-    return StringHasher::hashMemory<sizeof(FormElementKey)>(&key);
-}
-
-struct FormElementKeyHashTraits : HashTraits<FormElementKey> {
-    static void constructDeletedValue(FormElementKey& slot) { new (NotNull, &slot) FormElementKey(WTF::HashTableDeletedValue); }
-    static bool isDeletedValue(const FormElementKey& value) { return value.isHashTableDeletedValue(); }
-};
-
-// ----------------------------------------------------------------------------
-
-class SavedFormState {
-    WTF_MAKE_NONCOPYABLE(SavedFormState);
-    WTF_MAKE_FAST_ALLOCATED;
-
-public:
-    SavedFormState() = default;
-    static std::unique_ptr<SavedFormState> deserialize(const Vector<String>&, size_t& index);
-    void serializeTo(Vector<String>&) const;
-    bool isEmpty() const { return m_stateForNewFormElements.isEmpty(); }
-    void appendControlState(const AtomString& name, const AtomString& type, const FormControlState&);
-    FormControlState takeControlState(const AtomString& name, const AtomString& type);
-
-    Vector<String> referencedFilePaths() const;
-
 private:
-    HashMap<FormElementKey, Deque<FormControlState>, FormElementKeyHash, FormElementKeyHashTraits> m_stateForNewFormElements;
-    size_t m_controlStateCount { 0 };
+    HashMap<FormElementKey, Deque<FormControlState>> m_map;
 };
 
-static bool isNotFormControlTypeCharacter(UChar ch)
+FormController::SavedFormState FormController::SavedFormState::consumeSerializedState(StringVectorReader& reader)
 {
-    return !(ch == '-' || isASCIILower(ch));
-}
+    auto isNotFormControlTypeCharacter = [](UChar character) {
+        return !(character == '-' || isASCIILower(character));
+    };
 
-std::unique_ptr<SavedFormState> SavedFormState::deserialize(const Vector<String>& stateVector, size_t& index)
-{
-    if (index >= stateVector.size())
-        return nullptr;
-    auto itemCount = parseIntegerAllowingTrailingJunk<size_t>(stateVector[index++]).value_or(0);
-    if (!itemCount)
-        return nullptr;
-    auto savedFormState = makeUnique<SavedFormState>();
-    while (itemCount--) {
-        if (index + 1 >= stateVector.size())
-            return nullptr;
-        String name = stateVector[index++];
-        String type = stateVector[index++];
-        auto state = deserializeFormControlState(stateVector, index);
-        if (type.isEmpty() || type.find(isNotFormControlTypeCharacter) != notFound || !state)
-            return nullptr;
-        savedFormState->appendControlState(name, type, state.value());
+    SavedFormState result;
+    auto count = parseInteger<size_t>(reader.consumeString()).value_or(0);
+    while (count--) {
+        auto& name = reader.consumeString();
+        auto& type = reader.consumeString();
+        if (type.isEmpty() || StringView { type }.contains(isNotFormControlTypeCharacter))
+            return { };
+        auto state = consumeSerializedFormControlState(reader);
+        if (!state)
+            return { };
+        result.m_map.add({ name, type }, Deque<FormControlState> { }).iterator->value.append(WTFMove(*state));
     }
-    return savedFormState;
+    return result;
 }
 
-void SavedFormState::serializeTo(Vector<String>& stateVector) const
+FormControlState FormController::SavedFormState::takeControlState(const FormElementKey& key)
 {
-    stateVector.append(String::number(m_controlStateCount));
-    for (auto& element : m_stateForNewFormElements) {
-        const FormElementKey& key = element.key;
-        for (auto& controlState : element.value) {
-            stateVector.append(key.name());
-            stateVector.append(key.type());
-            serializeFormControlStateTo(controlState, stateVector);
-        }
-    }
-}
-
-void SavedFormState::appendControlState(const AtomString& name, const AtomString& type, const FormControlState& state)
-{
-    m_stateForNewFormElements.add(FormElementKey { name.impl(), type.impl() }, Deque<FormControlState> { }).iterator->value.append(state);
-    ++m_controlStateCount;
-}
-
-FormControlState SavedFormState::takeControlState(const AtomString& name, const AtomString& type)
-{
-    auto iterator = m_stateForNewFormElements.find(FormElementKey { name.impl(), type.impl() });
-    if (iterator == m_stateForNewFormElements.end())
+    auto iterator = m_map.find(key);
+    if (iterator == m_map.end())
         return { };
-
     auto state = iterator->value.takeFirst();
-    --m_controlStateCount;
     if (iterator->value.isEmpty())
-        m_stateForNewFormElements.remove(iterator);
+        m_map.remove(iterator);
     return state;
 }
 
-Vector<String> SavedFormState::referencedFilePaths() const
+void FormController::SavedFormState::appendReferencedFilePaths(Vector<String>& vector) const
 {
-    Vector<String> toReturn;
-    for (auto& element : m_stateForNewFormElements) {
-        if (!equal(element.key.type(), "file", 4))
+    for (auto& element : m_map) {
+        if (element.key.second != "file") // type
             continue;
         for (auto& state : element.value) {
             for (auto& file : HTMLInputElement::filesFromFileInputFormControlState(state))
-                toReturn.append(file.path);
+                vector.append(file.path);
         }
     }
-    return toReturn;
 }
 
 // ----------------------------------------------------------------------------
 
-class FormKeyGenerator {
+class FormController::FormKeyGenerator {
     WTF_MAKE_NONCOPYABLE(FormKeyGenerator);
     WTF_MAKE_FAST_ALLOCATED;
 
 public:
     FormKeyGenerator() = default;
-    AtomString formKey(const HTMLFormControlElementWithState&);
-    void willDeleteForm(HTMLFormElement*);
+    String formKey(const HTMLFormControlElementWithState&);
+    void willDeleteForm(HTMLFormElement&);
 
 private:
-    WeakHashMap<HTMLFormElement, AtomString> m_formToKeyMap;
+    WeakHashMap<HTMLFormElement, String> m_formToKeyMap;
     HashMap<String, unsigned> m_formSignatureToNextIndexMap;
 };
 
-static inline void recordFormStructure(const HTMLFormElement& form, StringBuilder& builder)
+static String formSignature(const HTMLFormElement& form)
 {
+    StringBuilder builder;
+
+    // Remove the query part because it might contain volatile parameters such as a session key.
+    // FIXME: But leave the fragment identifier? Perhaps we should switch to removeQueryAndFragmentIdentifier.
+
+    URL actionURL = form.getURLAttribute(actionAttr);
+    actionURL.setQuery({ });
+    builder.append(actionURL.string());
+
+    // Two named controls seems to be enough to distinguish similar but different forms.
+    constexpr unsigned maxNamedControlsToBeRecorded = 2;
+
     ScriptDisallowedScope::InMainThread scriptDisallowedScope;
-    // 2 is enough to distinguish forms in webkit.org/b/91209#c0
-    const size_t namedControlsToBeRecorded = 2;
-    auto& controls = form.unsafeAssociatedElements();
+    unsigned count = 0;
     builder.append(" [");
-    for (size_t i = 0, namedControls = 0; i < controls.size() && namedControls < namedControlsToBeRecorded; ++i) {
-        auto* formAssociatedElement = controls[i]->asFormAssociatedElement();
-        if (!formAssociatedElement->isFormControlElementWithState())
+    for (auto& control : form.unsafeAssociatedElements()) {
+        auto element = control->asFormAssociatedElement();
+        if (!is<HTMLFormControlElementWithState>(element))
             continue;
-        RefPtr<HTMLFormControlElementWithState> control = static_cast<HTMLFormControlElementWithState*>(formAssociatedElement);
-        if (!ownerFormForState(*control))
+        Ref controlWithState = downcast<HTMLFormControlElementWithState>(*element);
+        if (!ownerForm(controlWithState))
             continue;
-        AtomString name = control->name();
+        auto& name = controlWithState->name();
         if (name.isEmpty())
             continue;
-        namedControls++;
         builder.append(name, ' ');
+        if (++count >= maxNamedControlsToBeRecorded)
+            break;
     }
     builder.append(']');
-}
 
-static inline String formSignature(const HTMLFormElement& form)
-{
-    URL actionURL = form.getURLAttribute(actionAttr);
-    // Remove the query part because it might contain volatile parameters such
-    // as a session key.
-    actionURL.setQuery(String());
-    StringBuilder builder;
-    if (!actionURL.isEmpty())
-        builder.append(actionURL.string());
-
-    recordFormStructure(form, builder);
     return builder.toString();
 }
 
-AtomString FormKeyGenerator::formKey(const HTMLFormControlElementWithState& control)
+String FormController::FormKeyGenerator::formKey(const HTMLFormControlElementWithState& control)
 {
-    auto form = makeRefPtr(ownerFormForState(control));
+    RefPtr form = ownerForm(control);
     if (!form) {
-        static MainThreadNeverDestroyed<const AtomString> formKeyForNoOwner("No owner", AtomString::ConstructFromLiteral);
+        static MainThreadNeverDestroyed<String> formKeyForNoOwner(MAKE_STATIC_STRING_IMPL("No owner"));
         return formKeyForNoOwner;
     }
-
-    return m_formToKeyMap.ensure(*form, [this, &form] {
+    return m_formToKeyMap.ensure(*form, [this, form] {
         auto signature = formSignature(*form);
         auto nextIndex = m_formSignatureToNextIndexMap.add(signature, 0).iterator->value++;
-        // FIXME: Would be nice to have makeAtomString to use to optimize the case where the string already exists.
         return makeString(signature, " #", nextIndex);
     }).iterator->value;
 }
 
-void FormKeyGenerator::willDeleteForm(HTMLFormElement* form)
+void FormController::FormKeyGenerator::willDeleteForm(HTMLFormElement& form)
 {
-    RELEASE_ASSERT(form);
-    m_formToKeyMap.remove(*form);
+    m_formToKeyMap.remove(form);
 }
 
 // ----------------------------------------------------------------------------
@@ -345,111 +235,109 @@
 
 static String formStateSignature()
 {
-    // In the legacy version of serialized state, the first item was a name
-    // attribute value of a form control. The following string literal should
-    // contain some characters which are rarely used for name attribute values.
-    static NeverDestroyed<String> signature(MAKE_STATIC_STRING_IMPL("\n\r?% WebKit serialized form state version 8 \n\r=&"));
+    // In the legacy version of serialized state, the first item was a name attribute
+    // value of a form control. The following string literal contains some characters
+    // which are rarely used for name attribute values so it won't match.
+    static MainThreadNeverDestroyed<String> signature(MAKE_STATIC_STRING_IMPL("\n\r?% WebKit serialized form state version 8 \n\r=&"));
     return signature;
 }
 
-std::unique_ptr<FormController::SavedFormStateMap> FormController::createSavedFormStateMap(const FormControlVector& controlList)
+Vector<String> FormController::formElementsState(const Document& document) const
 {
-    FormKeyGenerator keyGenerator;
-    auto stateMap = makeUnique<SavedFormStateMap>();
-    for (const HTMLFormControlElementWithState& control : controlList) {
-        if (!control.shouldSaveAndRestoreFormControlState())
-            continue;
-        auto& formState = stateMap->add(keyGenerator.formKey(control).impl(), nullptr).iterator->value;
-        if (!formState)
-            formState = makeUnique<SavedFormState>();
-        formState->appendControlState(control.name(), control.type(), control.saveFormControlState());
-    }
-    return stateMap;
-}
+    struct Control {
+        Ref<const HTMLFormControlElementWithState> control;
+        String formKey;
+    };
 
-Vector<String> FormController::formElementsState(const Document& document) const
-{
-    // FIXME: We should be saving the state of form controls in shadow trees, too.
-    FormControlVector controls;
-    for (auto& control : descendantsOfType<HTMLFormControlElementWithState>(document)) {
-        ASSERT(control.insertionIndex());
-        controls.append(control);
+    Vector<Control> controls;
+    {
+        // FIXME: We should be saving the state of form controls in shadow trees, too.
+        FormKeyGenerator keyGenerator;
+        for (auto& control : descendantsOfType<HTMLFormControlElementWithState>(document)) {
+            ASSERT(control.insertionIndex());
+            if (control.shouldSaveAndRestoreFormControlState())
+                controls.append({ control, keyGenerator.formKey(control) });
+        }
     }
-
-    std::sort(controls.begin(), controls.end(), [](auto a, auto b) {
-        return a.get().insertionIndex() < b.get().insertionIndex();
+    if (controls.isEmpty())
+        return { };
+    std::sort(controls.begin(), controls.end(), [](auto& a, auto& b) {
+        if (a.formKey != b.formKey)
+            return codePointCompareLessThan(a.formKey, b.formKey);
+        return a.control->insertionIndex() < b.control->insertionIndex();
     });
 
-    auto stateMap = createSavedFormStateMap(controls);
     Vector<String> stateVector;
-    stateVector.reserveInitialCapacity(controls.size() * 4);
     stateVector.append(formStateSignature());
-    for (auto& state : *stateMap) {
-        stateVector.append(state.key.get());
-        state.value->serializeTo(stateVector);
+    for (size_t i = 0, size = controls.size(); i < size; ) {
+        auto formStart = i;
+        auto formKey = controls[formStart].formKey;
+        while (++i < size && controls[i].formKey == formKey) { }
+        stateVector.append(formKey);
+        stateVector.append(String::number(i - formStart));
+        for (size_t j = formStart; j < i; ++j) {
+            auto& control = controls[j].control.get();
+            stateVector.append(control.name());
+            stateVector.append(control.type());
+            appendSerializedFormControlState(stateVector, control.saveFormControlState());
+        }
     }
-    bool hasOnlySignature = stateVector.size() == 1;
-    if (hasOnlySignature)
-        stateVector.clear();
+    stateVector.shrinkToFit();
     return stateVector;
 }
 
 void FormController::setStateForNewFormElements(const Vector<String>& stateVector)
 {
-    formStatesFromStateVector(stateVector, m_savedFormStateMap);
+    m_savedFormStateMap = parseStateVector(stateVector);
 }
 
 FormControlState FormController::takeStateForFormElement(const HTMLFormControlElementWithState& control)
 {
     if (m_savedFormStateMap.isEmpty())
-        return FormControlState();
+        return { };
     if (!m_formKeyGenerator)
         m_formKeyGenerator = makeUnique<FormKeyGenerator>();
-    SavedFormStateMap::iterator it = m_savedFormStateMap.find(m_formKeyGenerator->formKey(control).impl());
-    if (it == m_savedFormStateMap.end())
-        return FormControlState();
-    FormControlState state = it->value->takeControlState(control.name(), control.type());
-    if (it->value->isEmpty())
-        m_savedFormStateMap.remove(it);
+    auto iterator = m_savedFormStateMap.find(m_formKeyGenerator->formKey(control));
+    if (iterator == m_savedFormStateMap.end())
+        return { };
+    auto state = iterator->value.takeControlState({ control.name(), control.type() });
+    if (iterator->value.isEmpty())
+        m_savedFormStateMap.remove(iterator);
     return state;
 }
 
-void FormController::formStatesFromStateVector(const Vector<String>& stateVector, SavedFormStateMap& map)
+FormController::SavedFormStateMap FormController::parseStateVector(const Vector<String>& stateVector)
 {
-    map.clear();
+    StringVectorReader reader { stateVector };
 
-    size_t i = 0;
-    if (stateVector.size() < 1 || stateVector[i++] != formStateSignature())
-        return;
+    if (reader.consumeString() != formStateSignature())
+        return { };
 
-    while (i + 1 < stateVector.size()) {
-        AtomString formKey = stateVector[i++];
-        auto state = SavedFormState::deserialize(stateVector, i);
-        if (!state) {
-            i = 0;
-            break;
-        }
-        map.add(formKey.impl(), WTFMove(state));
+    SavedFormStateMap map;
+    while (true) {
+        auto formKey = reader.consumeString();
+        if (formKey.isNull())
+            return map;
+        auto state = SavedFormState::consumeSerializedState(reader);
+        if (state.isEmpty())
+            return { };
+        map.add(formKey, WTFMove(state));
     }
-    if (i != stateVector.size())
-        map.clear();
 }
 
 void FormController::willDeleteForm(HTMLFormElement& form)
 {
     if (m_formKeyGenerator)
-        m_formKeyGenerator->willDeleteForm(&form);
+        m_formKeyGenerator->willDeleteForm(form);
 }
 
 void FormController::restoreControlStateFor(HTMLFormControlElementWithState& control)
 {
-    // We don't save state of a control with shouldSaveAndRestoreFormControlState()
-    // == false. But we need to skip restoring process too because a control in
+    // We don't save state of a control when shouldSaveAndRestoreFormControlState()
+    // is false. But we need to skip restoring process too because a control in
     // another form might have the same pair of name and type and saved its state.
-    if (!control.shouldSaveAndRestoreFormControlState())
+    if (!control.shouldSaveAndRestoreFormControlState() || ownerForm(control))
         return;
-    if (ownerFormForState(control))
-        return;
     auto state = takeStateForFormElement(control);
     if (!state.isEmpty())
         control.restoreFormControlState(state);
@@ -461,10 +349,8 @@
         if (!is<HTMLFormControlElementWithState>(element.get()))
             continue;
         auto& control = downcast<HTMLFormControlElementWithState>(element.get());
-        if (!control.shouldSaveAndRestoreFormControlState())
+        if (!control.shouldSaveAndRestoreFormControlState() || ownerForm(control) != &form)
             continue;
-        if (ownerFormForState(control) != &form)
-            continue;
         auto state = takeStateForFormElement(control);
         if (!state.isEmpty())
             control.restoreFormControlState(state);
@@ -479,10 +365,9 @@
 Vector<String> FormController::referencedFilePaths(const Vector<String>& stateVector)
 {
     Vector<String> paths;
-    SavedFormStateMap map;
-    formStatesFromStateVector(stateVector, map);
-    for (auto& state : map.values())
-        paths.appendVector(state->referencedFilePaths());
+    auto parsedState = parseStateVector(stateVector);
+    for (auto& state : parsedState.values())
+        state.appendReferencedFilePaths(paths);
     return paths;
 }
 

Modified: trunk/Source/WebCore/html/FormController.h (281504 => 281505)


--- trunk/Source/WebCore/html/FormController.h	2021-08-24 18:31:09 UTC (rev 281504)
+++ trunk/Source/WebCore/html/FormController.h	2021-08-24 18:32:59 UTC (rev 281505)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2021 Apple Inc. All rights reserved.
  * Copyright (C) 2010, 2011, 2012 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -22,29 +22,24 @@
 #pragma once
 
 #include <wtf/Forward.h>
-#include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
 class Document;
-class FormKeyGenerator;
 class HTMLFormControlElementWithState;
 class HTMLFormElement;
-class SavedFormState;
 
 using FormControlState = Vector<String>;
 
 class FormController {
     WTF_MAKE_FAST_ALLOCATED;
+
 public:
     FormController();
     ~FormController();
 
     Vector<String> formElementsState(const Document&) const;
-    void setStateForNewFormElements(const Vector<String>&);
+    void setStateForNewFormElements(const Vector<String>& stateVector);
 
     void willDeleteForm(HTMLFormElement&);
     void restoreControlStateFor(HTMLFormControlElementWithState&);
@@ -54,12 +49,12 @@
     WEBCORE_EXPORT static Vector<String> referencedFilePaths(const Vector<String>& stateVector);
 
 private:
-    using FormControlVector = Vector<std::reference_wrapper<const HTMLFormControlElementWithState>>;
-    using SavedFormStateMap = HashMap<RefPtr<AtomStringImpl>, std::unique_ptr<SavedFormState>>;
+    class FormKeyGenerator;
+    class SavedFormState;
+    using SavedFormStateMap = HashMap<String, SavedFormState>;
 
-    static std::unique_ptr<SavedFormStateMap> createSavedFormStateMap(const FormControlVector&);
     FormControlState takeStateForFormElement(const HTMLFormControlElementWithState&);
-    static void formStatesFromStateVector(const Vector<String>&, SavedFormStateMap&);
+    static SavedFormStateMap parseStateVector(const Vector<String>&);
 
     SavedFormStateMap m_savedFormStateMap;
     std::unique_ptr<FormKeyGenerator> m_formKeyGenerator;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to