Diff
Modified: trunk/LayoutTests/ChangeLog (121419 => 121420)
--- trunk/LayoutTests/ChangeLog 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/LayoutTests/ChangeLog 2012-06-28 08:48:20 UTC (rev 121420)
@@ -1,5 +1,20 @@
2012-06-28 Kent Tamura <[email protected]>
+ Classify form control states by their owner forms
+ https://bugs.webkit.org/show_bug.cgi?id=89950
+
+ Reviewed by Hajime Morita.
+
+ * 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.
+
+2012-06-28 Kent Tamura <[email protected]>
+
[Chromium] Test expectation update
* platform/chromium/TestExpectations:
Added: trunk/LayoutTests/fast/forms/resources/state-restore-per-form-back.html (0 => 121420)
--- trunk/LayoutTests/fast/forms/resources/state-restore-per-form-back.html (rev 0)
+++ trunk/LayoutTests/fast/forms/resources/state-restore-per-form-back.html 2012-06-28 08:48:20 UTC (rev 121420)
@@ -0,0 +1,16 @@
+<script>
+if (window.internals) {
+ buffer = 'Form state vector:';
+ var states = internals.formControlStateOfPreviousHistoryItem();
+ for (var i = 1; i < states.length; ++i) {
+ if (i % 5 == 1) {
+ console.log(buffer);
+ buffer = '';
+ }
+ buffer += states[i] + ', ';
+ }
+ console.log(buffer);
+ console.log('');
+}
+history.back();
+</script>
Modified: trunk/LayoutTests/fast/forms/state-restore-broken-state-expected.txt (121419 => 121420)
--- trunk/LayoutTests/fast/forms/state-restore-broken-state-expected.txt 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/LayoutTests/fast/forms/state-restore-broken-state-expected.txt 2012-06-28 08:48:20 UTC (rev 121420)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 5: Generated state: [name1,text,1,modified]
+CONSOLE MESSAGE: line 5: Generated state: [name1,text,state-restore-broken-state-2.html #0,1,modified]
The value was modified in the first load of state-restore-broken-state-1.html, but it should not be restored because the state-restore-broken-state-2.html breaks the state.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Added: trunk/LayoutTests/fast/forms/state-restore-per-form-expected.txt (0 => 121420)
--- trunk/LayoutTests/fast/forms/state-restore-per-form-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/forms/state-restore-per-form-expected.txt 2012-06-28 08:48:20 UTC (rev 121420)
@@ -0,0 +1,36 @@
+CONSOLE MESSAGE: line 7: Form state vector:
+CONSOLE MESSAGE: line 7: , text, No owner, 1, visited,
+CONSOLE MESSAGE: line 7: before, text, No owner, 1, form3-outer1-modified,
+CONSOLE MESSAGE: line 7: before, text, No owner, 1, noowner1-modified,
+CONSOLE MESSAGE: line 7: before, text, No owner, 1, form2-outer1-modified,
+CONSOLE MESSAGE: line 7: common-name1, text, state-restore-per-form-back.html #0, 0, common-name1,
+CONSOLE MESSAGE: line 7: text, state-restore-per-form-back.html #0, 1, form1-control2-modified, common-name1,
+CONSOLE MESSAGE: line 7: text, http://example.com/foo.cgi#bar #0, 1, query-changed-control1-modified, username,
+CONSOLE MESSAGE: line 7: text, http://example.com/foo.cgi#bar #1, 1, username-modified, common-name1,
+CONSOLE MESSAGE: line 7: text, http://example.com/foo.cgi#bar #1, 1, form3-control2-modified, after,
+CONSOLE MESSAGE: line 7: text, No owner, 1, form3-outer2-modified, after,
+CONSOLE MESSAGE: line 12: text, No owner, 1, form2-outer2-modified,
+CONSOLE MESSAGE: line 13:
+Confirm we can restore correctly even if the order of forms are changed:
+PASS $("form1-control1").value is "initial"
+PASS $("form1-control2").value is "form1-control2-modified"
+PASS $("form3-control2").value is "form3-control2-modified"
+
+Confirm we ignore the query part of action URLs:
+PASS $("form2-control1").value is "query-changed-control1-modified"
+
+Confirm an additional control in the previous form doesn't take a state for the next form:
+PASS $("form3-control1").value is "username-modified"
+PASS $("form2-added").value is "initial"
+
+Associated controls with form attributes are correctly handled:
+PASS $("form3-outer1").value is "form3-outer1-modified"
+FAIL $("form2-outer1").value should be form2-outer1-modified. Was noowner1-modified.
+FAIL $("noowner2").value should be initial. Was form3-outer2-modified.
+FAIL $("form3-outer2").value should be form3-outer2-modified. Was form2-outer2-modified.
+FAIL $("form2-outer2").value should be form2-outer2-modified. Was initial.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
Added: trunk/LayoutTests/fast/forms/state-restore-per-form.html (0 => 121420)
--- trunk/LayoutTests/fast/forms/state-restore-per-form.html (rev 0)
+++ trunk/LayoutTests/fast/forms/state-restore-per-form.html 2012-06-28 08:48:20 UTC (rev 121420)
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<div id="console"></div>
+
+<input id=emptyOnFirstVisit>
+
+<script>
+jsTestIsAsync = true;
+
+function makeForms(stage) {
+ var beforeForms = '<div id=parent>' +
+ '<input value=initial id=form3-outer1 name=before form=form3>' +
+ (stage == 1 ? '<input value=initial name=before id=noowner1>' : '') +
+ '<input value=initial id=form2-outer1 name=before form=form2>';
+
+ var backForm = '<form action="" id=form1>' +
+ '<input value=initial id=form1-control1 name=common-name1>' +
+ '<input value=initial id=form1-control2 name=common-name1>' +
+ '</form>';
+
+ var query = stage == 1 ? "?session=0123456" : "?session=7654321111";
+ var additionalControl = stage == 1 ? '' : '<input name=username id=form2-added value=initial>';
+ var sameActionForm1 = '<form action="" + query + '#bar" id=form2>' +
+ '<input value=initial id=form2-control1 name=common-name1>' +
+ additionalControl +
+ '</form>';
+
+ var sameActionForm2 = '<form action="" id=form3>' +
+ '<input value=initial id=form3-control1 name=username>' +
+ '<input value=initial id=form3-control2 name=common-name1>' +
+ '</form>';
+
+ var afterForms = (stage == 1 ? '' : '<input value=initial id=noowner2 name=after>') +
+ '<input value=initial id=form3-outer2 name=after form=form3>' +
+ '<input value=initial id=form2-outer2 name=after form=form2>' +
+ '</div>';
+
+ document.write(beforeForms +
+ (stage == 1 ? backForm + sameActionForm1 : sameActionForm1 + backForm) +
+ sameActionForm2 +
+ afterForms);
+}
+
+function runTest()
+{
+ var state = $('emptyOnFirstVisit');
+ if (!state.value) {
+ // First visit.
+ state.value = 'visited';
+ makeForms(1);
+
+ setTimeout(function() {
+ $('form1-control2').value = 'form1-control2-modified';
+ $('form2-control1').value = 'query-changed-control1-modified';
+ $('form3-control1').value = 'username-modified';
+ $('form3-control2').value = 'form3-control2-modified';
+ $('noowner1').value = 'noowner1-modified';
+ $('form2-outer1').value = 'form2-outer1-modified';
+ $('form3-outer1').value = 'form3-outer1-modified';
+ $('form2-outer2').value = 'form2-outer2-modified';
+ $('form3-outer2').value = 'form3-outer2-modified';
+ $('form1').submit();
+ }, 0);
+ } else {
+ // Second visit.
+ makeForms(2);
+
+ debug('Confirm we can restore correctly even if the order of forms are changed:');
+ shouldBeEqualToString('$("form1-control1").value', 'initial');
+ shouldBeEqualToString('$("form1-control2").value', 'form1-control2-modified');
+ shouldBeEqualToString('$("form3-control2").value', 'form3-control2-modified');
+
+ debug('\nConfirm we ignore the query part of action URLs:');
+ shouldBeEqualToString('$("form2-control1").value', 'query-changed-control1-modified');
+
+ debug('\nConfirm an additional control in the previous form doesn\'t take a state for the next form:');
+ shouldBeEqualToString('$("form3-control1").value', 'username-modified');
+ shouldBeEqualToString('$("form2-added").value', 'initial');
+
+ debug('\nAssociated controls with form attributes are correctly handled:');
+ // Some of the followings fail. They will be resolved in webkit.org/b/89962.
+ shouldBeEqualToString('$("form3-outer1").value', 'form3-outer1-modified');
+ shouldBeEqualToString('$("form2-outer1").value', 'form2-outer1-modified');
+
+ shouldBeEqualToString('$("noowner2").value', 'initial');
+ shouldBeEqualToString('$("form3-outer2").value', 'form3-outer2-modified');
+ shouldBeEqualToString('$("form2-outer2").value', 'form2-outer2-modified');
+
+ $('parent').innerHTML = '';
+ setTimeout(function() { finishJSTest(); }, 0);
+ }
+}
+
+runTest();
+</script>
+<script src=""
+</body>
Modified: trunk/Source/_javascript_Core/ChangeLog (121419 => 121420)
--- trunk/Source/_javascript_Core/ChangeLog 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/Source/_javascript_Core/ChangeLog 2012-06-28 08:48:20 UTC (rev 121420)
@@ -1,3 +1,13 @@
+2012-06-28 Kent Tamura <[email protected]>
+
+ Classify form control states by their owner forms
+ https://bugs.webkit.org/show_bug.cgi?id=89950
+
+ Reviewed by Hajime Morita.
+
+ * _javascript_Core.vcproj/_javascript_Core/_javascript_Core.def:
+ Expose WTF::StringBuilder::canShrink()
+
2012-06-27 Michael Saboff <[email protected]>
[Win] jscore-tests flakey
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcproj/_javascript_Core/_javascript_Core.def (121419 => 121420)
--- trunk/Source/_javascript_Core/_javascript_Core.vcproj/_javascript_Core/_javascript_Core.def 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcproj/_javascript_Core/_javascript_Core.def 2012-06-28 08:48:20 UTC (rev 121420)
@@ -85,6 +85,7 @@
?callHostFunctionAsConstructor@JSC@@YI_JPAVExecState@1@@Z
?callOnMainThread@WTF@@YAXP6AXPAX@Z0@Z
?callOnMainThreadAndWait@WTF@@YAXP6AXPAX@Z0@Z
+ ?canShrink@StringBuilder@WTF@@QBE_NXZ
?cancelCallOnMainThread@WTF@@YAXP6AXPAX@Z0@Z
?capacity@Heap@JSC@@QAEIXZ
?changePrototypeTransition@Structure@JSC@@SAPAV12@AAVJSGlobalData@2@PAV12@VJSValue@2@@Z
Modified: trunk/Source/WebCore/ChangeLog (121419 => 121420)
--- trunk/Source/WebCore/ChangeLog 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/Source/WebCore/ChangeLog 2012-06-28 08:48:20 UTC (rev 121420)
@@ -1,3 +1,56 @@
+2012-06-28 Kent Tamura <[email protected]>
+
+ Classify form control states by their owner forms
+ https://bugs.webkit.org/show_bug.cgi?id=89950
+
+ Reviewed by Hajime Morita.
+
+ 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.
+
2012-06-28 Sheriff Bot <[email protected]>
Unreviewed, rolling out r121395.
Modified: trunk/Source/WebCore/html/FormController.cpp (121419 => 121420)
--- trunk/Source/WebCore/html/FormController.cpp 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/Source/WebCore/html/FormController.cpp 2012-06-28 08:48:20 UTC (rev 121420)
@@ -22,6 +22,8 @@
#include "FormController.h"
#include "HTMLFormControlElementWithState.h"
+#include "HTMLFormElement.h"
+#include <wtf/text/StringBuilder.h>
namespace WebCore {
@@ -67,7 +69,75 @@
// ----------------------------------------------------------------------------
+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)
Modified: trunk/Source/WebCore/html/FormController.h (121419 => 121420)
--- trunk/Source/WebCore/html/FormController.h 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/Source/WebCore/html/FormController.h 2012-06-28 08:48:20 UTC (rev 121420)
@@ -32,17 +32,20 @@
namespace WebCore {
class FormAssociatedElement;
+class FormKeyGenerator;
class HTMLFormControlElementWithState;
+class HTMLFormElement;
class FormElementKey {
public:
- FormElementKey(AtomicStringImpl* = 0, AtomicStringImpl* = 0);
+ FormElementKey(AtomicStringImpl* = 0, AtomicStringImpl* = 0, AtomicStringImpl* = 0);
~FormElementKey();
FormElementKey(const FormElementKey&);
FormElementKey& operator=(const FormElementKey&);
AtomicStringImpl* name() const { return m_name; }
AtomicStringImpl* type() const { return m_type; }
+ AtomicStringImpl* formKey() const { return m_formKey; }
// Hash table deleted values, which are only constructed and never copied or destroyed.
FormElementKey(WTF::HashTableDeletedValueType) : m_name(hashTableDeletedValue()) { }
@@ -56,11 +59,12 @@
AtomicStringImpl* m_name;
AtomicStringImpl* m_type;
+ AtomicStringImpl* m_formKey;
};
inline bool operator==(const FormElementKey& a, const FormElementKey& b)
{
- return a.name() == b.name() && a.type() == b.type();
+ return a.name() == b.name() && a.type() == b.type() && a.formKey() == b.formKey();
}
struct FormElementKeyHash {
@@ -126,6 +130,7 @@
// This should be callled only by Document::setStateForNewFormElements().
void setStateForNewFormElements(const Vector<String>&);
FormControlState takeStateForFormElement(const HTMLFormControlElementWithState&);
+ void willDeleteForm(HTMLFormElement*);
void registerFormElementWithFormAttribute(FormAssociatedElement*);
void unregisterFormElementWithFormAttribute(FormAssociatedElement*);
@@ -143,7 +148,7 @@
typedef HashMap<FormElementKey, Deque<FormControlState>, FormElementKeyHash, FormElementKeyHashTraits> FormElementStateMap;
FormElementStateMap m_stateForNewFormElements;
-
+ OwnPtr<FormKeyGenerator> m_formKeyGenerator;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/html/HTMLFormElement.cpp (121419 => 121420)
--- trunk/Source/WebCore/html/HTMLFormElement.cpp 2012-06-28 08:44:50 UTC (rev 121419)
+++ trunk/Source/WebCore/html/HTMLFormElement.cpp 2012-06-28 08:48:20 UTC (rev 121420)
@@ -92,6 +92,7 @@
HTMLFormElement::~HTMLFormElement()
{
+ document()->formController()->willDeleteForm(this);
if (!shouldAutocomplete())
document()->unregisterForPageCacheSuspensionCallbacks(this);