Skip to content
Permalink
Browse files
Stop tracking form elements with FormController
https://bugs.webkit.org/show_bug.cgi?id=228724
<rdar://problem/81435095>

Reviewed by Darin Adler.

Source/WebCore:

FormController currently tracks the insertion order of
HTMLFormElementWithState objects in the document.  But we don't need
to know this list of form controls until the time we need to save
form state for the document (e.g. on pagehide).  So we instead
traverse the document to find those elements at the point we need
them, rather than maintain FormController::m_formElementsWithState.

This is a small speedup (1-2%) on a few of the Speedometer subtests
that insert and remove many input elements.

A future optimization could record on the Document whether there are
any input elements that have had their value changed, since it's
probably common for pages with form controls to never be changed.

* dom/Document.cpp:
(WebCore::Document::formElementsState const): Traverse the document to
find all the HTMLFormElementWithState objects.
* dom/Element.h:
(WebCore::Element::isFormControlElementWithState const):
* html/FormController.cpp:
(WebCore::FormController::createSavedFormStateMap):
(WebCore::FormController::formElementsState const):
* html/FormController.h:
* html/HTMLFormControlElementWithState.cpp:
(WebCore::HTMLFormControlElementWithState::insertedIntoAncestor):
Track the order that HTMLFormControlElementWithState objects are
inserted into the document.
(WebCore::HTMLFormControlElementWithState::removedFromAncestor):
* html/HTMLFormControlElementWithState.h:
(WebCore::HTMLFormControlElementWithState::insertionIndex const):
(isType):
* page/Frame.h: Remove unused formElementsCharacterCount.
* page/ios/FrameIOS.mm:

Source/WebKitLegacy/mac:

formElementsCharacterCount is unused and can be removed.

* WebView/WebFrame.mm:
* WebView/WebFramePrivate.h:


Canonical link: https://commits.webkit.org/240309@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280718 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
heycam committed Aug 6, 2021
1 parent 4ba37be commit 918e358163510b8e6169ed37fb2ac79443e51435
Showing 12 changed files with 91 additions and 60 deletions.
@@ -1,3 +1,45 @@
2021-08-05 Cameron McCormack <heycam@apple.com>

Stop tracking form elements with FormController
https://bugs.webkit.org/show_bug.cgi?id=228724
<rdar://problem/81435095>

Reviewed by Darin Adler.

FormController currently tracks the insertion order of
HTMLFormElementWithState objects in the document. But we don't need
to know this list of form controls until the time we need to save
form state for the document (e.g. on pagehide). So we instead
traverse the document to find those elements at the point we need
them, rather than maintain FormController::m_formElementsWithState.

This is a small speedup (1-2%) on a few of the Speedometer subtests
that insert and remove many input elements.

A future optimization could record on the Document whether there are
any input elements that have had their value changed, since it's
probably common for pages with form controls to never be changed.

* dom/Document.cpp:
(WebCore::Document::formElementsState const): Traverse the document to
find all the HTMLFormElementWithState objects.
* dom/Element.h:
(WebCore::Element::isFormControlElementWithState const):
* html/FormController.cpp:
(WebCore::FormController::createSavedFormStateMap):
(WebCore::FormController::formElementsState const):
* html/FormController.h:
* html/HTMLFormControlElementWithState.cpp:
(WebCore::HTMLFormControlElementWithState::insertedIntoAncestor):
Track the order that HTMLFormControlElementWithState objects are
inserted into the document.
(WebCore::HTMLFormControlElementWithState::removedFromAncestor):
* html/HTMLFormControlElementWithState.h:
(WebCore::HTMLFormControlElementWithState::insertionIndex const):
(isType):
* page/Frame.h: Remove unused formElementsCharacterCount.
* page/ios/FrameIOS.mm:

2021-08-05 Tim Horton <timothy_horton@apple.com>

fast/canvas/canvas-crash.html doesn't test what it intends to on iOS
@@ -1909,7 +1909,7 @@ Vector<String> Document::formElementsState() const
{
if (!m_formController)
return Vector<String>();
return m_formController->formElementsState();
return m_formController->formElementsState(*this);
}

void Document::setStateForNewFormElements(const Vector<String>& stateVector)
@@ -468,6 +468,7 @@ class Element : public ContainerNode {
#endif

virtual bool isFormControlElement() const { return false; }
virtual bool isFormControlElementWithState() const { return false; }
virtual bool isSpinButtonElement() const { return false; }
virtual bool isTextFormControlElement() const { return false; }
virtual bool isTextField() const { return false; }
@@ -342,16 +342,6 @@ FormController::FormController() = default;

FormController::~FormController() = default;

unsigned FormController::formElementsCharacterCount() const
{
unsigned count = 0;
for (auto& element : m_formElementsWithState) {
if (element->isTextField())
count += element->saveFormControlState()[0].length();
}
return count;
}

static String formStateSignature()
{
// In the legacy version of serialized state, the first item was a name
@@ -361,26 +351,37 @@ static String formStateSignature()
return signature;
}

std::unique_ptr<FormController::SavedFormStateMap> FormController::createSavedFormStateMap(const FormElementListHashSet& controlList)
std::unique_ptr<FormController::SavedFormStateMap> FormController::createSavedFormStateMap(const FormControlVector& controlList)
{
FormKeyGenerator keyGenerator;
auto stateMap = makeUnique<SavedFormStateMap>();
for (auto& control : controlList) {
if (!control->shouldSaveAndRestoreFormControlState())
for (const HTMLFormControlElementWithState& control : controlList) {
if (!control.shouldSaveAndRestoreFormControlState())
continue;
auto& formState = stateMap->add(keyGenerator.formKey(*control).impl(), nullptr).iterator->value;
auto& formState = stateMap->add(keyGenerator.formKey(control).impl(), nullptr).iterator->value;
if (!formState)
formState = makeUnique<SavedFormState>();
formState->appendControlState(control->name(), control->type(), control->saveFormControlState());
formState->appendControlState(control.name(), control.type(), control.saveFormControlState());
}
return stateMap;
}

Vector<String> FormController::formElementsState() const
Vector<String> FormController::formElementsState(const Document& document) const
{
std::unique_ptr<SavedFormStateMap> stateMap = createSavedFormStateMap(m_formElementsWithState);
// 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);
}

std::sort(controls.begin(), controls.end(), [](auto a, auto b) {
return a.get().insertionIndex() < b.get().insertionIndex();
});

auto stateMap = createSavedFormStateMap(controls);
Vector<String> stateVector;
stateVector.reserveInitialCapacity(m_formElementsWithState.size() * 4);
stateVector.reserveInitialCapacity(controls.size() * 4);
stateVector.append(formStateSignature());
for (auto& state : *stateMap) {
stateVector.append(state.key.get());
@@ -484,16 +485,4 @@ Vector<String> FormController::referencedFilePaths(const Vector<String>& stateVe
return paths;
}

void FormController::registerFormElementWithState(HTMLFormControlElementWithState& control)
{
ASSERT(!m_formElementsWithState.contains(&control));
m_formElementsWithState.add(&control);
}

void FormController::unregisterFormElementWithState(HTMLFormControlElementWithState& control)
{
ASSERT(m_formElementsWithState.contains(&control));
m_formElementsWithState.remove(&control);
}

} // namespace WebCore
@@ -42,12 +42,7 @@ class FormController {
FormController();
~FormController();

void registerFormElementWithState(HTMLFormControlElementWithState&);
void unregisterFormElementWithState(HTMLFormControlElementWithState&);

unsigned formElementsCharacterCount() const;

Vector<String> formElementsState() const;
Vector<String> formElementsState(const Document&) const;
void setStateForNewFormElements(const Vector<String>&);

void willDeleteForm(HTMLFormElement&);
@@ -58,14 +53,13 @@ class FormController {
WEBCORE_EXPORT static Vector<String> referencedFilePaths(const Vector<String>& stateVector);

private:
typedef ListHashSet<RefPtr<HTMLFormControlElementWithState>> FormElementListHashSet;
typedef HashMap<RefPtr<AtomStringImpl>, std::unique_ptr<SavedFormState>> SavedFormStateMap;
using FormControlVector = Vector<std::reference_wrapper<const HTMLFormControlElementWithState>>;
using SavedFormStateMap = HashMap<RefPtr<AtomStringImpl>, std::unique_ptr<SavedFormState>>;

static std::unique_ptr<SavedFormStateMap> createSavedFormStateMap(const FormElementListHashSet&);
static std::unique_ptr<SavedFormStateMap> createSavedFormStateMap(const FormControlVector&);
FormControlState takeStateForFormElement(const HTMLFormControlElementWithState&);
static void formStatesFromStateVector(const Vector<String>&, SavedFormStateMap&);

FormElementListHashSet m_formElementsWithState;
SavedFormStateMap m_savedFormStateMap;
std::unique_ptr<FormKeyGenerator> m_formKeyGenerator;
};
@@ -43,15 +43,13 @@ HTMLFormControlElementWithState::~HTMLFormControlElementWithState() = default;

Node::InsertedIntoAncestorResult HTMLFormControlElementWithState::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
{
if (insertionType.connectedToDocument && !containingShadowRoot())
document().formController().registerFormElementWithState(*this);
m_insertionIndex = ++lastInsertionIndex;
return HTMLFormControlElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
}

void HTMLFormControlElementWithState::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
{
if (removalType.disconnectedFromDocument && !containingShadowRoot() && !oldParentOfRemovedTree.containingShadowRoot())
document().formController().unregisterFormElementWithState(*this);
m_insertionIndex = 0;
HTMLFormControlElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
}

@@ -84,4 +82,6 @@ bool HTMLFormControlElementWithState::isFormControlElementWithState() const
return true;
}

uint64_t HTMLFormControlElementWithState::lastInsertionIndex { 0 };

} // namespace Webcore
@@ -38,6 +38,8 @@ class HTMLFormControlElementWithState : public HTMLFormControlElement {
virtual FormControlState saveFormControlState() const;
virtual void restoreFormControlState(const FormControlState&) { } // Called only if state is not empty.

uint64_t insertionIndex() const { return m_insertionIndex; }

protected:
HTMLFormControlElementWithState(const QualifiedName& tagName, Document&, HTMLFormElement*);

@@ -50,10 +52,15 @@ class HTMLFormControlElementWithState : public HTMLFormControlElement {

private:
bool isFormControlElementWithState() const final;

uint64_t m_insertionIndex { 0 };
static uint64_t lastInsertionIndex;
};

} // namespace WebCore

SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::HTMLFormControlElementWithState)
static bool isType(const WebCore::Element& element) { return element.isFormControlElementWithState(); }
static bool isType(const WebCore::Node& node) { return is<WebCore::Element>(node) && isType(downcast<WebCore::Element>(node)); }
static bool isType(const WebCore::FormAssociatedElement& element) { return element.isFormControlElementWithState(); }
SPECIALIZE_TYPE_TRAITS_END()
@@ -282,7 +282,6 @@ class Frame final : public AbstractFrame {
WEBCORE_EXPORT void updateLayout() const;
WEBCORE_EXPORT NSRect caretRect();
WEBCORE_EXPORT NSRect rectForScrollToVisible();
WEBCORE_EXPORT unsigned formElementsCharacterCount() const;

// This function is used by Legacy WebKit.
WEBCORE_EXPORT void setTimersPaused(bool);
@@ -627,14 +627,6 @@ static inline NodeQualifier ancestorRespondingToClickEventsNodeQualifier(Securit
return unionRect(selection.visibleStart().absoluteCaretBounds(), selection.visibleEnd().absoluteCaretBounds());
}

unsigned Frame::formElementsCharacterCount() const
{
Document* document = this->document();
if (!document)
return 0;
return document->formController().formElementsCharacterCount();
}

void Frame::setTimersPaused(bool paused)
{
if (!m_page)
@@ -1,3 +1,16 @@
2021-08-05 Cameron McCormack <heycam@apple.com>

Stop tracking form elements with FormController
https://bugs.webkit.org/show_bug.cgi?id=228724
<rdar://problem/81435095>

Reviewed by Darin Adler.

formElementsCharacterCount is unused and can be removed.

* WebView/WebFrame.mm:
* WebView/WebFramePrivate.h:

2021-07-29 Myles C. Maxfield <mmaxfield@apple.com>

Stop building WebGPU and the WHLSL compiler to decrease binary size
@@ -1163,11 +1163,6 @@ - (void)_recursive_pauseNullEventsForAllNetscapePlugins

#if PLATFORM(IOS_FAMILY)

- (unsigned)formElementsCharacterCount
{
return core(self)->formElementsCharacterCount();
}

- (void)setTimeoutsPaused:(BOOL)flag
{
if ([self _webHTMLDocumentView]) {
@@ -95,7 +95,6 @@ typedef enum {
- (BOOL)needsLayout; // Needed for Mail <rdar://problem/6228038>
- (void)_setLoadsSynchronously:(BOOL)flag;
- (BOOL)_loadsSynchronously;
- (unsigned)formElementsCharacterCount;
- (void)setTimeoutsPaused:(BOOL)flag;

/*!

0 comments on commit 918e358

Please sign in to comment.