Classify form control states by their owner forms
https://bugs.webkit.org/show_bug.cgi?id=89950

Reviewed by Hajime Morita.

Source/JavaScriptCore:

* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
Expose WTF::StringBuilder::canShrink()

Source/WebCore:

To improve robustness of the form state restore feature, we classify
form control states by their owner forms. Owner forms are identified by
their action URLs and index numbers in forms with the same action URLs.

Implementation approach:
Extend FormElementKey class to have "formKey" string, which is a
combination of the action URL and an index number, or a fixed string for
no form owner.
FormKeyGenerator class is responsible to generate the "formKey" strings

Test: fast/forms/state-restore-per-form.html

* html/FormController.cpp:
(FormKeyGenerator):
(WebCore::FormKeyGenerator::create): A factory function.
(WebCore::FormKeyGenerator::FormKeyGenerator): A private constructor.
(WebCore::createKey):
A helper for formKey(). This makes strings like "<action URL> #<index>".
(WebCore::FormKeyGenerator::formKey):
Returns a formKey for the specified HTMLFormElement*.
(WebCore::FormKeyGenerator::willDeleteForm):
Unregister HTMLFormElement*. This function is necessary because form
restore feature works during parsing and a script might delete form
elements.
(WebCore::formStateSignature): Bump the version.
(WebCore::FormController::formElementsState):
Records a formKey string for each of control state.
(WebCore::FormController::setStateForNewFormElements):
Loads formKeys from stateVector, and uses them for FormElementKey.
(WebCore::FormController::takeStateForFormElement):
- Construct and destruct FormKeyGenerator if needed.
- Passing a formKey for the specified form control to FormElementKey.
(WebCore::FormController::willDeleteForm):
Delegate to FormKeyGenerator::willDeleteForm.

(WebCore::FormElementKey::FormElementKey): Add formKey argument and member.
(WebCore::FormElementKey::operator=): ditto.
(WebCore::FormElementKey::ref): ditto.
(WebCore::FormElementKey::deref): ditto.
* html/FormController.h:
(FormElementKey): Add formKey argument and member.
(FormController): Add a FormKeyGenerator member which is used during restoring.

* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::~HTMLFormElement): Notify the death to FormController.

LayoutTests:

* fast/forms/resources/state-restore-per-form-back.html: Added.
* fast/forms/state-restore-per-form-expected.txt:
Added. This contains some FAIL lines. They are expected and will
be fixed in webkit.org/b/89962.
* fast/forms/state-restore-per-form.html: Added.
* fast/forms/state-restore-broken-state-expected.txt:
Updated for the serialization format change.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@121420 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/html/FormController.cpp b/Source/WebCore/html/FormController.cpp
index 8719443..b7c38e2 100644
--- a/Source/WebCore/html/FormController.cpp
+++ b/Source/WebCore/html/FormController.cpp
@@ -22,6 +22,8 @@
 #include "FormController.h"
 
 #include "HTMLFormControlElementWithState.h"
+#include "HTMLFormElement.h"
+#include <wtf/text/StringBuilder.h>
 
 namespace WebCore {
 
@@ -67,6 +69,74 @@
 
 // ----------------------------------------------------------------------------
 
+class FormKeyGenerator {
+    WTF_MAKE_NONCOPYABLE(FormKeyGenerator);
+    WTF_MAKE_FAST_ALLOCATED;
+
+public:
+    static PassOwnPtr<FormKeyGenerator> create() { return adoptPtr(new FormKeyGenerator); }
+    AtomicString formKey(const HTMLFormControlElementWithState&);
+    void willDeleteForm(HTMLFormElement*);
+
+private:
+    FormKeyGenerator() { }
+
+    typedef HashMap<HTMLFormElement*, AtomicString> FormToKeyMap;
+    FormToKeyMap m_formToKeyMap;
+    HashSet<AtomicString> m_existingKeys;
+};
+
+static inline AtomicString createKey(HTMLFormElement* form, unsigned index)
+{
+    ASSERT(form);
+    KURL 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());
+    builder.append(" #");
+    builder.append(String::number(index));
+    return builder.toAtomicString();
+}
+
+AtomicString FormKeyGenerator::formKey(const HTMLFormControlElementWithState& control)
+{
+    // Assume contorl with form attribute have no owners because we restores
+    // state during parsing and form owners of such controls might be
+    // indeterminate.
+    HTMLFormElement* form = control.fastHasAttribute(formAttr) ? 0 : control.form();
+    if (!form) {
+        DEFINE_STATIC_LOCAL(AtomicString, formKeyForNoOwner, ("No owner"));
+        return formKeyForNoOwner;
+    }
+    FormToKeyMap::const_iterator it = m_formToKeyMap.find(form);
+    if (it != m_formToKeyMap.end())
+        return it->second;
+
+    AtomicString candidateKey;
+    unsigned index = 0;
+    do {
+        candidateKey = createKey(form, index++);
+    } while (!m_existingKeys.add(candidateKey).isNewEntry);
+    m_formToKeyMap.add(form, candidateKey);
+    return candidateKey;
+}
+
+void FormKeyGenerator::willDeleteForm(HTMLFormElement* form)
+{
+    ASSERT(form);
+    if (m_formToKeyMap.isEmpty())
+        return;
+    FormToKeyMap::iterator it = m_formToKeyMap.find(form);
+    if (it == m_formToKeyMap.end())
+        return;
+    m_existingKeys.remove(it->second);
+    m_formToKeyMap.remove(it);
+}
+
+// ----------------------------------------------------------------------------
 
 FormController::FormController()
 {
@@ -81,14 +151,15 @@
     // 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.
-    DEFINE_STATIC_LOCAL(String, signature, ("\n\r?% WebKit serialized form state version 3 \n\r=&"));
+    DEFINE_STATIC_LOCAL(String, signature, ("\n\r?% WebKit serialized form state version 5 \n\r=&"));
     return signature;
 }
 
 Vector<String> FormController::formElementsState() const
 {
+    OwnPtr<FormKeyGenerator> keyGenerator = FormKeyGenerator::create();
     Vector<String> stateVector;
-    stateVector.reserveInitialCapacity(m_formElementsWithState.size() * 4 + 1);
+    stateVector.reserveInitialCapacity(m_formElementsWithState.size() * 5 + 1);
     stateVector.append(formStateSignature());
     typedef FormElementListHashSet::const_iterator Iterator;
     Iterator end = m_formElementsWithState.end();
@@ -98,6 +169,7 @@
             continue;
         stateVector.append(elementWithState->name().string());
         stateVector.append(elementWithState->formControlType().string());
+        stateVector.append(keyGenerator->formKey(*elementWithState).string());
         elementWithState->saveFormControlState().serializeTo(stateVector);
     }
     return stateVector;
@@ -120,11 +192,12 @@
     while (i + 2 < stateVector.size()) {
         AtomicString name = stateVector[i++];
         AtomicString type = stateVector[i++];
+        AtomicString formKey = stateVector[i++];
         FormControlState state = FormControlState::deserialize(stateVector, i);
         if (type.isEmpty() || type.impl()->find(isNotFormControlTypeCharacter) != notFound || state.isFailure())
             break;
 
-        FormElementKey key(name.impl(), type.impl());
+        FormElementKey key(name.impl(), type.impl(), formKey.impl());
         Iterator it = m_stateForNewFormElements.find(key);
         if (it != m_stateForNewFormElements.end())
             it->second.append(state);
@@ -142,17 +215,28 @@
 {
     if (m_stateForNewFormElements.isEmpty())
         return FormControlState();
+    if (!m_formKeyGenerator)
+        m_formKeyGenerator = FormKeyGenerator::create();
     typedef FormElementStateMap::iterator Iterator;
-    Iterator it = m_stateForNewFormElements.find(FormElementKey(control.name().impl(), control.type().impl()));
+    Iterator it = m_stateForNewFormElements.find(FormElementKey(control.name().impl(), control.type().impl(), m_formKeyGenerator->formKey(control).impl()));
     if (it == m_stateForNewFormElements.end())
         return FormControlState();
     ASSERT(it->second.size());
     FormControlState state = it->second.takeFirst();
-    if (!it->second.size())
+    if (!it->second.size()) {
         m_stateForNewFormElements.remove(it);
+        if (m_stateForNewFormElements.isEmpty())
+            m_formKeyGenerator.clear();
+    }
     return state;
 }
 
+void FormController::willDeleteForm(HTMLFormElement* form)
+{
+    if (m_formKeyGenerator)
+        m_formKeyGenerator->willDeleteForm(form);
+}
+
 void FormController::registerFormElementWithFormAttribute(FormAssociatedElement* element)
 {
     ASSERT(toHTMLElement(element)->fastHasAttribute(formAttr));
@@ -172,8 +256,10 @@
         (*it)->resetFormOwner();
 }
 
-FormElementKey::FormElementKey(AtomicStringImpl* name, AtomicStringImpl* type)
-    : m_name(name), m_type(type)
+FormElementKey::FormElementKey(AtomicStringImpl* name, AtomicStringImpl* type, AtomicStringImpl* formKey)
+    : m_name(name)
+    , m_type(type)
+    , m_formKey(formKey)
 {
     ref();
 }
@@ -184,7 +270,9 @@
 }
 
 FormElementKey::FormElementKey(const FormElementKey& other)
-    : m_name(other.name()), m_type(other.type())
+    : m_name(other.name())
+    , m_type(other.type())
+    , m_formKey(other.formKey())
 {
     ref();
 }
@@ -195,6 +283,7 @@
     deref();
     m_name = other.name();
     m_type = other.type();
+    m_formKey = other.formKey();
     return *this;
 }
 
@@ -204,6 +293,8 @@
         name()->ref();
     if (type())
         type()->ref();
+    if (formKey())
+        formKey()->ref();
 }
 
 void FormElementKey::deref() const
@@ -212,6 +303,8 @@
         name()->deref();
     if (type())
         type()->deref();
+    if (formKey())
+        formKey()->deref();
 }
 
 unsigned FormElementKeyHash::hash(const FormElementKey& key)