Backed out 2 changesets (bug 1553705) for causing Bug1562142 . CLOSED TREE
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Sat, 29 Jun 2019 02:39:01 +0300
changeset 480657 1ddc47f7370187b3a07d61a65f9f0fd19e7e20a5
parent 480656 10ff2986b3eac0272f633e46811d9e76a79d75e1
child 480658 45488fb0fb4fbc279430886edbba4b01a3c63104
push id113561
push userncsoregi@mozilla.com
push dateSat, 29 Jun 2019 10:06:30 +0000
treeherdermozilla-inbound@84318ef3fa4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1553705, 1562142
milestone69.0a1
backs outfbb26a04ec1f69879c85249da04fe3b971928ff0
dd6e7c0970d5a55be8f8ca98dd3b3af6267b5ea1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 2 changesets (bug 1553705) for causing Bug1562142 . CLOSED TREE Backed out changeset fbb26a04ec1f (bug 1553705) Backed out changeset dd6e7c0970d5 (bug 1553705)
accessible/generic/Accessible.cpp
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/html/HTMLButtonElement.cpp
dom/html/HTMLFormElement.cpp
dom/html/HTMLFormElement.h
dom/html/HTMLInputElement.cpp
dom/html/HTMLSelectElement.cpp
dom/html/HTMLTextAreaElement.cpp
dom/html/nsGenericHTMLElement.cpp
dom/html/nsGenericHTMLElement.h
dom/html/nsIFormControl.h
layout/base/nsFrameManager.cpp
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
layout/generic/nsIStatefulFrame.h
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -33,17 +33,16 @@
 
 #include "nsIDOMXULButtonElement.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsINodeList.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/dom/Document.h"
-#include "mozilla/dom/HTMLFormElement.h"
 #include "nsIContent.h"
 #include "nsIForm.h"
 #include "nsIFormControl.h"
 
 #include "nsDeckFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsIStringBundle.h"
 #include "nsPresContext.h"
@@ -1726,17 +1725,18 @@ Relation Accessible::RelationByType(Rela
     case RelationType::PARENT_WINDOW_OF:
       return Relation();
 
     case RelationType::DEFAULT_BUTTON: {
       if (mContent->IsHTMLElement()) {
         // HTML form controls implements nsIFormControl interface.
         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
         if (control) {
-          if (dom::HTMLFormElement* form = control->GetFormElement()) {
+          nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
+          if (form) {
             nsCOMPtr<nsIContent> formContent =
                 do_QueryInterface(form->GetDefaultSubmitElement());
             return Relation(mDoc, formContent);
           }
         }
       } else {
         // In XUL, use first <button default="true" .../> in the document
         dom::Document* doc = mContent->OwnerDoc();
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1333,19 +1333,17 @@ Document::Document(const char* aContentT
       mServoRestyleRootDirtyBits(0),
       mThrowOnDynamicMarkupInsertionCounter(0),
       mIgnoreOpensDuringUnloadCounter(0),
       mDocLWTheme(Doc_Theme_Uninitialized),
       mSavedResolution(1.0f),
       mPendingInitialTranslation(false),
       mGeneration(0),
       mCachedTabSizeGeneration(0),
-      mInRDMPane(false),
-      mNextFormNumber(0),
-      mNextControlNumber(0) {
+      mInRDMPane(false) {
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
 
   SetIsInDocument();
   SetIsConnected(true);
 
   if (StaticPrefs::layout_css_use_counters_enabled()) {
     mStyleUseCounters = Servo_UseCounters_Create().Consume();
   }
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -1692,28 +1692,16 @@ class Document : public nsINode,
 
   /**
    * Called when this Document's editor is destroyed.
    */
   void TearingDownEditor();
 
   void SetKeyPressEventModel(uint16_t aKeyPressEventModel);
 
-  // Gets the next form number.
-  //
-  // Used by nsContentUtils::GenerateStateKey to get a unique number for each
-  // parser inserted form element.
-  int32_t GetNextFormNumber() { return mNextFormNumber++; }
-
-  // Gets the next form control number.
-  //
-  // Used by nsContentUtils::GenerateStateKey to get a unique number for each
-  // parser inserted form control element.
-  int32_t GetNextControlNumber() { return mNextControlNumber++; }
-
  protected:
   friend class nsUnblockOnloadEvent;
 
   nsresult InitCSP(nsIChannel* aChannel);
 
   nsresult InitFeaturePolicy(nsIChannel* aChannel);
 
   nsresult InitReferrerInfo(nsIChannel* aChannel);
@@ -5204,20 +5192,16 @@ class Document : public nsINode,
   int32_t mCachedTabSizeGeneration;
   nsTabSizes mCachedTabSizes;
 
   bool mInRDMPane;
 
   // The principal to use for the storage area of this document.
   nsCOMPtr<nsIPrincipal> mIntrinsicStoragePrincipal;
 
-  // See GetNextFormNumber and GetNextControlNumber.
-  int32_t mNextFormNumber;
-  int32_t mNextControlNumber;
-
  public:
   // Needs to be public because the bindings code pokes at it.
   js::ExpandoAndGeneration mExpandoAndGeneration;
 
   bool HasPendingInitialTranslation() { return mPendingInitialTranslation; }
 
   void TraceProtos(JSTracer* aTrc);
 };
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2663,160 +2663,128 @@ static inline void KeyAppendInt(int32_t 
 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
   return aContent->IsElement() &&
          aContent->AsElement()->AttrValueIs(
              kNameSpaceID_None, nsGkAtoms::autocomplete,
              NS_LITERAL_STRING("off"), eIgnoreCase);
 }
 
 /*static*/
-void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
-                                      nsACString& aKey) {
-  MOZ_ASSERT(aContent);
-
+nsresult nsContentUtils::GenerateStateKey(nsIContent* aContent,
+                                          Document* aDocument,
+                                          nsACString& aKey) {
   aKey.Truncate();
 
   uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
 
+  // We must have content if we're not using a special state id
+  NS_ENSURE_TRUE(aContent, NS_ERROR_FAILURE);
+
   // Don't capture state for anonymous content
   if (aContent->IsInAnonymousSubtree()) {
-    return;
+    return NS_OK;
   }
 
   if (IsAutocompleteOff(aContent)) {
-    return;
+    return NS_OK;
   }
 
   RefPtr<Document> doc = aContent->GetUncomposedDoc();
 
   KeyAppendInt(partID, aKey);  // first append a partID
   bool generatedUniqueKey = false;
 
   if (doc && doc->IsHTMLOrXHTML()) {
     nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
+    RefPtr<nsContentList> htmlForms;
+    RefPtr<nsContentList> htmlFormControls;
+    htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
+                                     getter_AddRefs(htmlFormControls));
 
     // If we have a form control and can calculate form information, use that
     // as the key - it is more reliable than just recording position in the
     // DOM.
     // XXXbz Is it, really?  We have bugs on this, I think...
     // Important to have a unique key, and tag/type/name may not be.
     //
-    // The format of the key depends on whether the control has a form,
-    // and whether the element was parser inserted:
-    //
-    // [Has Form, Parser Inserted]:
-    //   fp>type>FormNum>IndOfControlInForm>FormName>name
-    //
-    // [No Form, Parser Inserted]:
-    //   dp>type>ControlNum>name
-    //
-    // [Has Form, Not Parser Inserted]:
-    //   fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
-    //
-    // [No Form, Not Parser Inserted]:
-    //   dn>type>IndOfControlInDoc>name
+    // If the control has a form, the format of the key is:
+    // f>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
+    // else:
+    // d>type>IndOfControlInDoc>name
     //
     // XXX We don't need to use index if name is there
     // XXXbz We don't?  Why not?  I don't follow.
     //
     nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
     if (control) {
-      // Get the control number if this was a parser inserted element from the
-      // network.
-      int32_t controlNumber =
-          control->GetParserInsertedControlNumberForStateKey();
-      bool parserInserted = controlNumber != -1;
-
-      RefPtr<nsContentList> htmlForms;
-      RefPtr<nsContentList> htmlFormControls;
-      if (!parserInserted) {
-        // Getting these lists is expensive, as we need to keep them up to date
-        // as the document loads, so we avoid it if we don't need them.
-        htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
-                                         getter_AddRefs(htmlFormControls));
-      }
-
       // Append the control type
       KeyAppendInt(control->ControlType(), aKey);
 
       // If in a form, add form name / index of form / index in form
-      HTMLFormElement* formElement = control->GetFormElement();
+      Element* formElement = control->GetFormElement();
       if (formElement) {
         if (IsAutocompleteOff(formElement)) {
           aKey.Truncate();
-          return;
+          return NS_OK;
         }
 
-        // Append the form number, if this is a parser inserted control, or
-        // the index of the form in the document otherwise.
-        bool appendedForm = false;
-        if (parserInserted) {
-          MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
-                     "when generating a state key for a parser inserted form "
-                     "control we should have a parser inserted <form> element");
-          KeyAppendString(NS_LITERAL_CSTRING("fp"), aKey);
-          KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
-          appendedForm = true;
-        } else {
-          KeyAppendString(NS_LITERAL_CSTRING("fn"), aKey);
-          int32_t index = htmlForms->IndexOf(formElement, false);
-          if (index <= -1) {
-            //
-            // XXX HACK this uses some state that was dumped into the document
-            // specifically to fix bug 138892.  What we are trying to do is
-            // *guess* which form this control's state is found in, with the
-            // highly likely guess that the highest form parsed so far is the
-            // one. This code should not be on trunk, only branch.
-            //
-            index = htmlDoc->GetNumFormsSynchronous() - 1;
-          }
-          if (index > -1) {
-            KeyAppendInt(index, aKey);
-            appendedForm = true;
-          }
+        KeyAppendString(NS_LITERAL_CSTRING("f"), aKey);
+
+        // Append the index of the form in the document
+        int32_t index = htmlForms->IndexOf(formElement, false);
+        if (index <= -1) {
+          //
+          // XXX HACK this uses some state that was dumped into the document
+          // specifically to fix bug 138892.  What we are trying to do is
+          // *guess* which form this control's state is found in, with the
+          // highly likely guess that the highest form parsed so far is the one.
+          // This code should not be on trunk, only branch.
+          //
+          index = htmlDoc->GetNumFormsSynchronous() - 1;
         }
-
-        if (appendedForm) {
+        if (index > -1) {
+          KeyAppendInt(index, aKey);
+
           // Append the index of the control in the form
-          int32_t index = formElement->IndexOfControl(control);
+          nsCOMPtr<nsIForm> form(do_QueryInterface(formElement));
+          index = form->IndexOfControl(control);
 
           if (index > -1) {
             KeyAppendInt(index, aKey);
             generatedUniqueKey = true;
           }
         }
 
         // Append the form name
         nsAutoString formName;
         formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
         KeyAppendString(formName, aKey);
+
       } else {
-        // Not in a form.  Append the control number, if this is a parser
-        // inserted control, or the index of the control in the document
-        // otherwise.
-        if (parserInserted) {
-          KeyAppendString(NS_LITERAL_CSTRING("dp"), aKey);
-          KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
-                       aKey);
+        KeyAppendString(NS_LITERAL_CSTRING("d"), aKey);
+
+        // If not in a form, add index of control in document
+        // Less desirable than indexing by form info.
+
+        // Hash by index of control in doc (we are not in a form)
+        // These are important as they are unique, and type/name may not be.
+
+        // We have to flush sink notifications at this point to make
+        // sure that htmlFormControls is up to date.
+        int32_t index = htmlFormControls->IndexOf(aContent, true);
+        if (index > -1) {
+          KeyAppendInt(index, aKey);
           generatedUniqueKey = true;
-        } else {
-          KeyAppendString(NS_LITERAL_CSTRING("dn"), aKey);
-          int32_t index = htmlFormControls->IndexOf(aContent, true);
-          if (index > -1) {
-            KeyAppendInt(index, aKey);
-            generatedUniqueKey = true;
-          }
         }
-
-        // Append the control name
-        nsAutoString name;
-        aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
-                                       name);
-        KeyAppendString(name, aKey);
       }
+
+      // Append the control name
+      nsAutoString name;
+      aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
+      KeyAppendString(name, aKey);
     }
   }
 
   if (!generatedUniqueKey) {
     // Either we didn't have a form control or we aren't in an HTML document so
     // we can't figure out form info.  Append the tag name if it's an element
     // to avoid restoring state for one type of element on another type.
     if (aContent->IsElement()) {
@@ -2834,16 +2802,18 @@ void nsContentUtils::GenerateStateKey(ns
     nsINode* parent = aContent->GetParentNode();
     nsINode* content = aContent;
     while (parent) {
       KeyAppendInt(parent->ComputeIndexOf(content), aKey);
       content = parent;
       parent = content->GetParentNode();
     }
   }
+
+  return NS_OK;
 }
 
 // static
 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
   MOZ_ASSERT(NS_IsMainThread());
 
   // As opposed to SubjectPrincipal(), we do in fact assume that
   // we're in a realm here; anyone who calls this function in
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -718,18 +718,18 @@ class nsContentUtils {
   static nsIPrincipal* SubjectPrincipal();
 
   // Returns the prinipal of the given JS object. This may only be called on
   // the main thread for objects from the main thread's JSRuntime. The object
   // must not be a cross-compartment wrapper, because CCWs are not associated
   // with a single realm.
   static nsIPrincipal* ObjectPrincipal(JSObject* aObj);
 
-  static void GenerateStateKey(nsIContent* aContent, Document* aDocument,
-                               nsACString& aKey);
+  static nsresult GenerateStateKey(nsIContent* aContent, Document* aDocument,
+                                   nsACString& aKey);
 
   /**
    * Create a new nsIURI from aSpec, using aBaseURI as the base.  The
    * origin charset of the new nsIURI will be the document charset of
    * aDocument.
    */
   static nsresult NewURIWithDocumentCharset(nsIURI** aResult,
                                             const nsAString& aSpec,
--- a/dom/html/HTMLButtonElement.cpp
+++ b/dom/html/HTMLButtonElement.cpp
@@ -49,17 +49,17 @@ static const nsAttrValue::EnumTable kBut
 
 // Default type is 'submit'.
 static const nsAttrValue::EnumTable* kButtonDefaultType = &kButtonTypeTable[2];
 
 // Construction, destruction
 HTMLButtonElement::HTMLButtonElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     FromParser aFromParser)
-    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo), aFromParser,
+    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo),
                                         kButtonDefaultType->value),
       mDisabledChanged(false),
       mInInternalActivate(false),
       mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)) {
   // Set up our default state: enabled
   AddStatesSilently(NS_EVENT_STATE_ENABLED);
 }
 
@@ -340,19 +340,21 @@ HTMLButtonElement::SubmitNamesValues(HTM
 
   //
   // Submit
   //
   return aFormSubmission->AddNameValuePair(name, value);
 }
 
 void HTMLButtonElement::DoneCreatingElement() {
-  GenerateStateKey();
   if (!mInhibitStateRestoration) {
-    RestoreFormControlState();
+    nsresult rv = GenerateStateKey();
+    if (NS_SUCCEEDED(rv)) {
+      RestoreFormControlState();
+    }
   }
 }
 
 nsresult HTMLButtonElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
                                           const nsAttrValueOrString* aValue,
                                           bool aNotify) {
   if (aNotify && aName == nsGkAtoms::disabled &&
       aNameSpaceID == kNameSpaceID_None) {
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -105,17 +105,16 @@ HTMLFormElement::HTMLFormElement(
       mSubmittingRequest(nullptr),
       mDefaultSubmitElement(nullptr),
       mFirstSubmitInElements(nullptr),
       mFirstSubmitNotInElements(nullptr),
       mImageNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
       mPastNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
       mSubmitPopupState(PopupBlocker::openAbused),
       mInvalidElementsCount(0),
-      mFormNumber(-1),
       mGeneratingSubmit(false),
       mGeneratingReset(false),
       mIsSubmitting(false),
       mDeferSubmission(false),
       mNotifiedObservers(false),
       mNotifiedObserversResult(false),
       mEverTriedInvalidSubmit(false) {
   // We start out valid.
@@ -2308,31 +2307,10 @@ void HTMLFormElement::RemoveElementFromP
   }
 }
 
 JSObject* HTMLFormElement::WrapNode(JSContext* aCx,
                                     JS::Handle<JSObject*> aGivenProto) {
   return HTMLFormElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-int32_t HTMLFormElement::GetFormNumberForStateKey() {
-  if (mFormNumber == -1) {
-    mFormNumber = OwnerDoc()->GetNextFormNumber();
-  }
-  return mFormNumber;
-}
-
-void HTMLFormElement::NodeInfoChanged(Document* aOldDoc) {
-  nsGenericHTMLElement::NodeInfoChanged(aOldDoc);
-
-  // When a <form> element is adopted into a new document, we want any state
-  // keys generated from it to no longer consider this element to be parser
-  // inserted, and so have state keys based on the position of the <form>
-  // element in the document, rather than the order it was inserted in.
-  //
-  // This is not strictly necessary, since we only ever look at the form number
-  // for parser inserted form controls, and we do that at the time the form
-  // control element is inserted into its original document by the parser.
-  mFormNumber = -1;
-}
-
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -515,27 +515,16 @@ class HTMLFormElement final : public nsG
    * Get the full URL to submit to.  Do not submit if the returned URL is null.
    *
    * @param aActionURL the full, unadulterated URL you'll be submitting to [OUT]
    * @param aOriginatingElement the originating element of the form submission
    * [IN]
    */
   nsresult GetActionURL(nsIURI** aActionURL, Element* aOriginatingElement);
 
-  // Returns a number for this form that is unique within its owner document.
-  // This is used by nsContentUtils::GenerateStateKey to identify form controls
-  // that are inserted into the document by the parser.
-  int32_t GetFormNumberForStateKey();
-
-  /**
-   * Called when we have been cloned and adopted, and the information of the
-   * node has been changed.
-   */
-  void NodeInfoChanged(Document* aOldDoc) override;
-
  protected:
   //
   // Data members
   //
   /** The list of controls (form.elements as well as stuff not in elements) */
   RefPtr<HTMLFormControlsCollection> mControls;
   /** The currently selected radio button of each group */
   nsRefPtrHashtable<nsStringHashKey, HTMLInputElement> mSelectedRadioButtons;
@@ -586,19 +575,16 @@ class HTMLFormElement final : public nsG
 
   /**
    * Number of invalid and candidate for constraint validation elements in the
    * form the last time UpdateValidity has been called.
    * @note Should only be used by UpdateValidity() and GetValidity()!
    */
   int32_t mInvalidElementsCount;
 
-  // See GetFormNumberForStateKey.
-  int32_t mFormNumber;
-
   /** Whether we are currently processing a submit event or not */
   bool mGeneratingSubmit;
   /** Whether we are currently processing a reset event or not */
   bool mGeneratingReset;
   /** Whether we are submitting currently */
   bool mIsSubmitting;
   /** Whether the submission is to be deferred in case a script triggers it */
   bool mDeferSubmission;
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -934,17 +934,17 @@ void HTMLInputElement::Shutdown() {
 
 //
 // construction, destruction
 //
 
 HTMLInputElement::HTMLInputElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     FromParser aFromParser, FromClone aFromClone)
-    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo), aFromParser,
+    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo),
                                         kInputDefaultType->value),
       mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
       mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
       mDisabledChanged(false),
       mValueChanged(false),
       mLastValueChangeWasInteractive(false),
       mCheckedChanged(false),
       mChecked(false),
@@ -5889,18 +5889,19 @@ HTMLInputElement::SaveState() {
 
 void HTMLInputElement::DoneCreatingElement() {
   mDoneCreating = true;
 
   //
   // Restore state as needed.  Note that disabled state applies to all control
   // types.
   //
-  GenerateStateKey();
-  bool restoredCheckedState = !mInhibitRestoration && RestoreFormControlState();
+  bool restoredCheckedState = !mInhibitRestoration &&
+                              NS_SUCCEEDED(GenerateStateKey()) &&
+                              RestoreFormControlState();
 
   //
   // If restore does not occur, we initialize .checked using the CHECKED
   // property.
   //
   if (!restoredCheckedState && mShouldInitChecked) {
     DoSetChecked(DefaultChecked(), false, false);
   }
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -106,18 +106,17 @@ SafeOptionListMutation::~SafeOptionListM
 // HTMLSelectElement
 //
 
 // construction, destruction
 
 HTMLSelectElement::HTMLSelectElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     FromParser aFromParser)
-    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo), aFromParser,
-                                        NS_FORM_SELECT),
+    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo), NS_FORM_SELECT),
       mOptions(new HTMLOptionsCollection(this)),
       mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
       mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
       mIsDoneAddingChildren(!aFromParser),
       mDisabledChanged(false),
       mMutating(false),
       mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
       mSelectionHasChanged(false),
@@ -1108,19 +1107,21 @@ void HTMLSelectElement::DoneAddingChildr
     mRestoreState = nullptr;
   }
 
   // Notify the frame
   if (selectFrame) {
     selectFrame->DoneAddingChildren(true);
   }
 
-  GenerateStateKey();
   if (!mInhibitStateRestoration) {
-    RestoreFormControlState();
+    nsresult rv = GenerateStateKey();
+    if (NS_SUCCEEDED(rv)) {
+      RestoreFormControlState();
+    }
   }
 
   // Now that we're done, select something (if it's a single select something
   // must be selected)
   if (!CheckSelectSomething(false)) {
     // If an option has @selected set, it will be selected during parsing but
     // with an empty value. We have to make sure the select element updates it's
     // validity state to take this into account.
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -46,18 +46,17 @@
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
 namespace mozilla {
 namespace dom {
 
 HTMLTextAreaElement::HTMLTextAreaElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     FromParser aFromParser)
-    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo), aFromParser,
-                                        NS_FORM_TEXTAREA),
+    : nsGenericHTMLFormElementWithState(std::move(aNodeInfo), NS_FORM_TEXTAREA),
       mValueChanged(false),
       mLastValueChangeWasInteractive(false),
       mHandlingSelect(false),
       mDoneAddingChildren(!aFromParser),
       mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
       mDisabledChanged(false),
       mCanShowInvalidUI(true),
       mCanShowValidUI(true),
@@ -513,19 +512,21 @@ nsresult HTMLTextAreaElement::PostHandle
 void HTMLTextAreaElement::DoneAddingChildren(bool aHaveNotified) {
   if (!mValueChanged) {
     if (!mDoneAddingChildren) {
       // Reset now that we're done adding children if the content sink tried to
       // sneak some text in without calling AppendChildTo.
       Reset();
     }
 
-    GenerateStateKey();
     if (!mInhibitStateRestoration) {
-      RestoreFormControlState();
+      nsresult rv = GenerateStateKey();
+      if (NS_SUCCEEDED(rv)) {
+        RestoreFormControlState();
+      }
     }
   }
 
   mDoneAddingChildren = true;
 }
 
 bool HTMLTextAreaElement::IsDoneAddingChildren() { return mDoneAddingChildren; }
 
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1606,17 +1606,17 @@ void nsGenericHTMLFormElement::ClearForm
   }
 
   UnsetFlags(ADDED_TO_FORM);
   mForm = nullptr;
 
   AfterClearForm(aUnbindOrDelete);
 }
 
-HTMLFormElement* nsGenericHTMLFormElement::GetFormElement() { return mForm; }
+Element* nsGenericHTMLFormElement::GetFormElement() { return mForm; }
 
 HTMLFieldSetElement* nsGenericHTMLFormElement::GetFieldSet() {
   return mFieldSet;
 }
 
 nsIContent::IMEState nsGenericHTMLFormElement::GetDesiredIMEState() {
   TextEditor* textEditor = GetTextEditorInternal();
   if (!textEditor) {
@@ -2515,46 +2515,47 @@ void nsGenericHTMLElement::ChangeEditabl
       htmlEditor->NotifyEditingHostMaybeChanged();
     }
   }
 }
 
 //----------------------------------------------------------------------
 
 nsGenericHTMLFormElementWithState::nsGenericHTMLFormElementWithState(
-    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-    FromParser aFromParser, uint8_t aType)
-    : nsGenericHTMLFormElement(std::move(aNodeInfo), aType),
-      mControlNumber(!!(aFromParser & FROM_PARSER_NETWORK)
-                         ? OwnerDoc()->GetNextControlNumber()
-                         : -1) {
+    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, uint8_t aType)
+    : nsGenericHTMLFormElement(std::move(aNodeInfo), aType) {
   mStateKey.SetIsVoid(true);
 }
 
-void nsGenericHTMLFormElementWithState::GenerateStateKey() {
+nsresult nsGenericHTMLFormElementWithState::GenerateStateKey() {
   // Keep the key if already computed
   if (!mStateKey.IsVoid()) {
-    return;
+    return NS_OK;
   }
 
   Document* doc = GetUncomposedDoc();
   if (!doc) {
-    mStateKey.Truncate();
-    return;
+    return NS_OK;
   }
 
   // Generate the state key
-  nsContentUtils::GenerateStateKey(this, doc, mStateKey);
+  nsresult rv = nsContentUtils::GenerateStateKey(this, doc, mStateKey);
+
+  if (NS_FAILED(rv)) {
+    mStateKey.SetIsVoid(true);
+    return rv;
+  }
 
   // If the state key is blank, this is anonymous content or for whatever
   // reason we are not supposed to save/restore state: keep it as such.
   if (!mStateKey.IsEmpty()) {
     // Add something unique to content so layout doesn't muck us up.
     mStateKey += "-C";
   }
+  return NS_OK;
 }
 
 PresState* nsGenericHTMLFormElementWithState::GetPrimaryPresState() {
   if (mStateKey.IsEmpty()) {
     return nullptr;
   }
 
   nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(false);
@@ -2592,19 +2593,16 @@ nsGenericHTMLFormElementWithState::GetLa
   if (aRead && !history->HasStates()) {
     return nullptr;
   }
 
   return history.forget();
 }
 
 bool nsGenericHTMLFormElementWithState::RestoreFormControlState() {
-  MOZ_ASSERT(!mStateKey.IsVoid(),
-             "GenerateStateKey must already have been called");
-
   if (mStateKey.IsEmpty()) {
     return false;
   }
 
   nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(true);
   if (!history) {
     return false;
   }
@@ -2617,22 +2615,16 @@ bool nsGenericHTMLFormElementWithState::
     return result;
   }
 
   return false;
 }
 
 void nsGenericHTMLFormElementWithState::NodeInfoChanged(Document* aOldDoc) {
   nsGenericHTMLElement::NodeInfoChanged(aOldDoc);
-
-  // We need to regenerate the state key now we're in a new document.  Clearing
-  // mControlNumber means we stop considering this control to be parser
-  // inserted, and we'll generate a state key based on its position in the
-  // document rather than the order it was inserted into the document.
-  mControlNumber = -1;
   mStateKey.SetIsVoid(true);
 }
 
 nsSize nsGenericHTMLElement::GetWidthHeightForImage(
     RefPtr<imgRequestProxy>& aImageRequest) {
   nsSize size(0, 0);
 
   nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -925,17 +925,17 @@ class nsGenericHTMLFormElement : public 
 
   nsINode* GetScopeChainParent() const override;
 
   virtual bool IsNodeOfType(uint32_t aFlags) const override;
   virtual void SaveSubtreeState() override;
 
   // nsIFormControl
   virtual mozilla::dom::HTMLFieldSetElement* GetFieldSet() override;
-  virtual mozilla::dom::HTMLFormElement* GetFormElement() override;
+  virtual mozilla::dom::Element* GetFormElement() override;
   mozilla::dom::HTMLFormElement* GetForm() const { return mForm; }
   virtual void SetForm(mozilla::dom::HTMLFormElement* aForm) override;
   virtual void ClearForm(bool aRemoveFromForm, bool aUnbindOrDelete) override;
 
   NS_IMETHOD SaveState() override { return NS_OK; }
 
   virtual bool RestoreState(mozilla::PresState* aState) override {
     return false;
@@ -1078,18 +1078,17 @@ class nsGenericHTMLFormElement : public 
 
   /* This is a pointer to our closest fieldset parent if any */
   mozilla::dom::HTMLFieldSetElement* mFieldSet;
 };
 
 class nsGenericHTMLFormElementWithState : public nsGenericHTMLFormElement {
  public:
   nsGenericHTMLFormElementWithState(
-      already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
-      mozilla::dom::FromParser aFromParser, uint8_t aType);
+      already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, uint8_t aType);
 
   /**
    * Get the presentation state for a piece of content, or create it if it does
    * not exist.  Generally used by SaveState().
    */
   mozilla::PresState* GetPrimaryPresState();
 
   /**
@@ -1097,50 +1096,38 @@ class nsGenericHTMLFormElementWithState 
    *
    * @param aRead if true, won't return a layout history state if the
    *              layout history state is empty.
    * @return the history state object
    */
   already_AddRefed<nsILayoutHistoryState> GetLayoutHistory(bool aRead);
 
   /**
+   * Restore the state for a form control.  Ends up calling
+   * nsIFormControl::RestoreState().
+   *
+   * @return false if RestoreState() was not called, the return
+   *         value of RestoreState() otherwise.
+   */
+  bool RestoreFormControlState();
+
+  /**
    * Called when we have been cloned and adopted, and the information of the
    * node has been changed.
    */
   virtual void NodeInfoChanged(Document* aOldDoc) override;
 
  protected:
-  /**
-   * Restore the state for a form control in response to the element being
-   * inserted into the document by the parser.  Ends up calling
-   * nsIFormControl::RestoreState().
-   *
-   * GenerateStateKey() must already have been called.
-   *
-   * @return false if RestoreState() was not called, the return
-   *         value of RestoreState() otherwise.
-   */
-  bool RestoreFormControlState();
-
   /* Generates the state key for saving the form state in the session if not
-     computed already. The result is stored in mStateKey. */
-  void GenerateStateKey();
-
-  int32_t GetParserInsertedControlNumberForStateKey() const override {
-    return mControlNumber;
-  }
+     computed already. The result is stored in mStateKey on success */
+  nsresult GenerateStateKey();
 
   /* Used to store the key to that element in the session. Is void until
      GenerateStateKey has been used */
   nsCString mStateKey;
-
-  // A number for this form control that is unique within its owner document.
-  // This is only set to a number for elements inserted into the document by
-  // the parser from the network.  Otherwise, it is -1.
-  int32_t mControlNumber;
 };
 
 #define NS_INTERFACE_MAP_ENTRY_IF_TAG(_interface, _tag) \
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(_interface,        \
                                      mNodeInfo->Equals(nsGkAtoms::_tag))
 
 namespace mozilla {
 namespace dom {
--- a/dom/html/nsIFormControl.h
+++ b/dom/html/nsIFormControl.h
@@ -102,17 +102,17 @@ class nsIFormControl : public nsISupport
    * @return the fieldset
    */
   virtual mozilla::dom::HTMLFieldSetElement* GetFieldSet() = 0;
 
   /**
    * Get the form for this form control.
    * @return the form
    */
-  virtual mozilla::dom::HTMLFormElement* GetFormElement() = 0;
+  virtual mozilla::dom::Element* GetFormElement() = 0;
 
   /**
    * Set the form for this form control.
    * @param aForm the form.  This must not be null.
    *
    * @note that when setting the form the control is not added to the
    * form.  It adds itself when it gets bound to the tree thereafter,
    * so that it can be properly sorted with the other controls in the
@@ -217,26 +217,16 @@ class nsIFormControl : public nsISupport
    * @return whether this form control can have draggable children.
    */
   inline bool AllowDraggableChildren() const;
 
   virtual bool IsDisabledForEvents(mozilla::WidgetEvent* aEvent) {
     return false;
   }
 
-  // Returns a number for this form control that is unique within its
-  // owner document.  This is used by nsContentUtils::GenerateStateKey
-  // to identify form controls that are inserted into the document by
-  // the parser.  -1 is returned for form controls with no state or
-  // which were inserted into the document by some other means than
-  // the parser from the network.
-  virtual int32_t GetParserInsertedControlNumberForStateKey() const {
-    return -1;
-  };
-
  protected:
   /**
    * Returns whether mType corresponds to a single line text control type.
    * @param aExcludePassword to have NS_FORM_INPUT_PASSWORD ignored.
    * @param aType the type to be tested.
    * @return whether mType corresponds to a single line text control type.
    */
   inline static bool IsSingleLineTextControl(bool aExcludePassword,
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -141,18 +141,18 @@ void nsFrameManager::CaptureFrameStateFo
     return;
   }
 
   // Generate the hash key to store the state under
   // Exit early if we get empty key
   nsAutoCString stateKey;
   nsIContent* content = aFrame->GetContent();
   Document* doc = content ? content->GetUncomposedDoc() : nullptr;
-  statefulFrame->GenerateStateKey(content, doc, stateKey);
-  if (stateKey.IsEmpty()) {
+  nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey);
+  if (NS_FAILED(rv) || stateKey.IsEmpty()) {
     return;
   }
 
   // Store the state. aState owns frameState now.
   aState->AddState(stateKey, std::move(frameState));
 }
 
 void nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
@@ -202,29 +202,29 @@ void nsFrameManager::RestoreFrameStateFo
   // If we don't have content, we can't generate a hash
   // key and there's probably no state information for us.
   if (!content) {
     return;
   }
 
   nsAutoCString stateKey;
   Document* doc = content->GetUncomposedDoc();
-  statefulFrame->GenerateStateKey(content, doc, stateKey);
-  if (stateKey.IsEmpty()) {
+  nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey);
+  if (NS_FAILED(rv) || stateKey.IsEmpty()) {
     return;
   }
 
   // Get the state from the hash
   PresState* frameState = aState->GetState(stateKey);
   if (!frameState) {
     return;
   }
 
   // Restore it
-  nsresult rv = statefulFrame->RestoreState(frameState);
+  rv = statefulFrame->RestoreState(frameState);
   if (NS_FAILED(rv)) {
     return;
   }
 
   // If we restore ok, remove the state from the state table
   aState->RemoveState(stateKey);
 }
 
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -1564,24 +1564,26 @@ nsComboboxControlFrame::RestoreState(Pre
   }
   ShowList(aState->droppedDown());  // might destroy us
   return NS_OK;
 }
 
 // Append a suffix so that the state key for the combobox is different
 // from the state key the list control uses to sometimes save the scroll
 // position for the same Element
-void nsComboboxControlFrame::GenerateStateKey(nsIContent* aContent,
-                                              Document* aDocument,
-                                              nsACString& aKey) {
-  nsContentUtils::GenerateStateKey(aContent, aDocument, aKey);
-  if (aKey.IsEmpty()) {
-    return;
+NS_IMETHODIMP
+nsComboboxControlFrame::GenerateStateKey(nsIContent* aContent,
+                                         Document* aDocument,
+                                         nsACString& aKey) {
+  nsresult rv = nsContentUtils::GenerateStateKey(aContent, aDocument, aKey);
+  if (NS_FAILED(rv) || aKey.IsEmpty()) {
+    return rv;
   }
   aKey.AppendLiteral("CCF");
+  return NS_OK;
 }
 
 // Fennec uses a custom combobox built-in widget.
 //
 
 /* static */
 bool nsComboboxControlFrame::ToolkitHasNativePopup() {
 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -204,18 +204,19 @@ class nsComboboxControlFrame final : pub
   }
 
   virtual nsIWidget* GetRollupWidget() override;
 
   // nsIStatefulFrame
   mozilla::UniquePtr<mozilla::PresState> SaveState() override;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD RestoreState(mozilla::PresState* aState) override;
-  void GenerateStateKey(nsIContent* aContent, mozilla::dom::Document* aDocument,
-                        nsACString& aKey) override;
+  NS_IMETHOD GenerateStateKey(nsIContent* aContent,
+                              mozilla::dom::Document* aDocument,
+                              nsACString& aKey) override;
 
   static bool ToolkitHasNativePopup();
 
  protected:
   friend class RedisplayTextEvent;
   friend class nsAsyncResize;
   friend class nsResizeDropdownAtFinalPosition;
 
--- a/layout/generic/nsIStatefulFrame.h
+++ b/layout/generic/nsIStatefulFrame.h
@@ -25,16 +25,16 @@ class nsIStatefulFrame {
 
   // Save the state for this frame.
   virtual mozilla::UniquePtr<mozilla::PresState> SaveState() = 0;
 
   // Restore the state for this frame from aState
   NS_IMETHOD RestoreState(mozilla::PresState* aState) = 0;
 
   // Generate a key for this stateful frame
-  virtual void GenerateStateKey(nsIContent* aContent,
-                                mozilla::dom::Document* aDocument,
-                                nsACString& aKey) {
-    nsContentUtils::GenerateStateKey(aContent, aDocument, aKey);
+  NS_IMETHOD GenerateStateKey(nsIContent* aContent,
+                              mozilla::dom::Document* aDocument,
+                              nsACString& aKey) {
+    return nsContentUtils::GenerateStateKey(aContent, aDocument, aKey);
   };
 };
 
 #endif /* _nsIStatefulFrame_h */