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;