Bug 215405. Restore scroll position, but not form state, on no-store and SSL no-cache sites. r+sr=bzbarsky
authorGraeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
Wed, 03 Dec 2008 12:55:14 -0500
changeset 22262 4047bed537d67f61567d6fa546da47cdab3dda35
parent 22261 f290559c01d1686789ede6c58feeaa518e79e21a
child 22263 27d2163fccfd235f3522f97121b91700c4a9cb1c
push id3885
push userbzbarsky@mozilla.com
push dateWed, 03 Dec 2008 17:56:13 +0000
treeherdermozilla-central@4047bed537d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs215405
milestone1.9.2a1pre
Bug 215405. Restore scroll position, but not form state, on no-store and SSL no-cache sites. r+sr=bzbarsky
content/html/content/src/nsHTMLSelectElement.cpp
docshell/base/nsDocShell.cpp
docshell/shistory/src/nsSHEntry.cpp
docshell/test/chrome/215405_nocache.html
docshell/test/chrome/215405_nocache.html^headers^
docshell/test/chrome/215405_nostore.html
docshell/test/chrome/215405_nostore.html^headers^
docshell/test/chrome/Makefile.in
docshell/test/chrome/bug215405_window.xul
docshell/test/chrome/test_bug215405.xul
layout/base/nsDocumentViewer.cpp
layout/base/nsFrameManager.cpp
layout/base/nsILayoutHistoryState.h
layout/base/nsLayoutHistoryState.cpp
layout/base/nsPresState.cpp
layout/base/nsPresState.h
layout/forms/nsIsIndexFrame.cpp
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1514,17 +1514,17 @@ nsHTMLSelectElement::SaveState()
 
 PRBool
 nsHTMLSelectElement::RestoreState(nsPresState* aState)
 {
   // Get the presentation state object to retrieve our stuff out of.
   nsCOMPtr<nsISupports> state;
   nsresult rv = aState->GetStatePropertyAsSupports(NS_LITERAL_STRING("selecteditems"),
                                                    getter_AddRefs(state));
-  if (NS_SUCCEEDED(rv)) {
+  if (rv == NS_STATE_PROPERTY_EXISTS) {
     RestoreStateTo((nsSelectState*)(nsISupports*)state);
 
     // Don't flush, if the frame doesn't exist yet it doesn't care if
     // we're reset or not.
     DispatchContentReset();
   }
 
   nsAutoString disabled;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8485,21 +8485,16 @@ NS_IMETHODIMP nsDocShell::GetShouldSaveL
     return NS_OK;
 }
 
 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
 {
     nsresult  rv = NS_OK;
     
     if (mOSHE) {
-        PRBool shouldSave;
-        GetShouldSaveLayoutState(&shouldSave);
-        if (!shouldSave)
-            return NS_OK;
-
         nsCOMPtr<nsIPresShell> shell;
         rv = GetPresShell(getter_AddRefs(shell));
         if (NS_SUCCEEDED(rv) && shell) {
             nsCOMPtr<nsILayoutHistoryState> layoutState;
             rv = shell->CaptureHistoryState(getter_AddRefs(layoutState),
                                             PR_TRUE);
         }
     }
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -345,18 +345,20 @@ NS_IMETHODIMP nsSHEntry::GetLayoutHistor
 {
   *aResult = mLayoutHistoryState;
   NS_IF_ADDREF(*aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsSHEntry::SetLayoutHistoryState(nsILayoutHistoryState* aState)
 {
-  NS_ENSURE_STATE(mSaveLayoutState || !aState);
   mLayoutHistoryState = aState;
+  if (mLayoutHistoryState)
+    mLayoutHistoryState->SetScrollPositionOnly(!mSaveLayoutState);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP nsSHEntry::GetLoadType(PRUint32 * aResult)
 {
   *aResult = mLoadType;
   return NS_OK;
 }
@@ -420,21 +422,18 @@ NS_IMETHODIMP nsSHEntry::GetSaveLayoutSt
 {
   *aFlag = mSaveLayoutState;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsSHEntry::SetSaveLayoutStateFlag(PRBool  aFlag)
 {
   mSaveLayoutState = aFlag;
-
-  // if we are not allowed to hold layout history state, then make sure
-  // that we are not holding it!
-  if (!mSaveLayoutState)
-    mLayoutHistoryState = nsnull;
+  if (mLayoutHistoryState)
+    mLayoutHistoryState->SetScrollPositionOnly(!aFlag);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsSHEntry::GetExpirationStatus(PRBool * aFlag)
 {
   *aFlag = mExpired;
   return NS_OK;
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/215405_nocache.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+  <title>test1</title>
+</head>
+<body style="height: 100%">
+  <input type="text" id="inp" value="">
+  </input>
+  <div style="height: 50%">Some text</div>
+  <div style="height: 50%">Some text</div>
+  <div style="height: 50%">Some text</div>
+  <div style="height: 50%; width: 300%">Some more text</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/215405_nocache.html^headers^
@@ -0,0 +1,1 @@
+Cache-control: no-cache
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/215405_nostore.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+  <title>test1</title>
+</head>
+<body style="height: 100%">
+  <input type="text" id="inp" value="">
+  </input>
+  <div style="height: 50%">Some text</div>
+  <div style="height: 50%">Some text</div>
+  <div style="height: 50%">Some text</div>
+  <div style="height: 50%; width: 350%">Some more text</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/215405_nostore.html^headers^
@@ -0,0 +1,1 @@
+Cache-control: no-store
--- a/docshell/test/chrome/Makefile.in
+++ b/docshell/test/chrome/Makefile.in
@@ -43,26 +43,32 @@ relativesrcdir	= docshell/test/chrome
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _HTTP_FILES =	\
 		92598_nostore.html \
 		92598_nostore.html^headers^ \
 		112564_nocache.html \
 		112564_nocache.html^headers^ \
+		215405_nostore.html \
+		215405_nostore.html^headers^ \
+		215405_nocache.html \
+		215405_nocache.html^headers^ \
 		$(NULL)
 
 _TEST_FILES =	\
 		test_bug92598.xul \
 		bug92598_window.xul \
 		92598_nostore.html \
 		test_bug112564.xul \
 		bug112564_window.xul \
 		test_bug113934.xul \
 		bug113934_window.xul \
+		test_bug215405.xul \
+		bug215405_window.xul \
 		test_bug364461.xul \
 		bug364461_window.xul \
 		test_bug396519.xul \
 		bug396519_window.xul \
 		test_bug428288.html \
 		test_bug454235.xul \
 		bug454235-subframe.xul \
 		test_bug456980.xul \
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/bug215405_window.xul
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is bug 215405 test code
+   -
+   - The Initial Developer of the Original Code is
+   - Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>.
+   - Portions created by the Initial Developer are Copyright (C) 2008
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="215405Test"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        width="600"
+        height="600"
+        onload="onLoad();"
+        title="215405 test">
+
+  <script type="application/javascript"><![CDATA[
+    var imports = [ "SimpleTest", "is", "isnot", "ok"];
+    for each (var import in imports) {
+      window[import] = window.opener.wrappedJSObject[import];
+    }
+
+    const text="MOZILLA";
+    const nostoreURI = "http://localhost:8888/tests/docshell/test/chrome/" +
+                       "215405_nostore.html";
+    const nocacheURI = "https://example.com:443/tests/docshell/test/chrome/" +
+                       "215405_nocache.html";
+
+    var gBrowser;
+    var gTestsIterator;
+    var scrollX = 0;
+    var scrollY = 0;
+
+    function finish() {
+      gBrowser.removeEventListener("pageshow", eventListener, true);
+      window.close();
+      window.opener.wrappedJSObject.SimpleTest.finish();
+    }
+
+    function onLoad(e) {
+      gBrowser = document.getElementById("content");
+      gBrowser.addEventListener("pageshow", eventListener, true);
+       
+      gTestsIterator = testsIterator();
+      nextTest();
+    }
+
+    function eventListener(event) {
+      setTimeout(nextTest, 0);
+    }
+
+    function nextTest() {
+      try {
+        gTestsIterator.next();
+      } catch (err if err instanceof StopIteration) {
+        finish();
+      }
+    }
+
+    function testsIterator() {
+      // No-store tests
+      var testName = "[nostore]";
+
+      // Load a page with a no-store header
+      gBrowser.loadURI(nostoreURI);
+      yield;
+
+
+      // Now that the page has loaded, amend the form contents
+      var form = gBrowser.contentDocument.getElementById("inp");
+      form.value = text;
+
+      // Attempt to scroll the page
+      var originalXPosition = gBrowser.contentWindow.scrollX;
+      var originalYPosition = gBrowser.contentWindow.scrollY;
+      var scrollToX = gBrowser.contentWindow.scrollMaxX;
+      var scrollToY = gBrowser.contentWindow.scrollMaxY;
+      gBrowser.contentWindow.scrollBy(scrollToX, scrollToY);
+      
+      // Save the scroll position for future comparison
+      scrollX = gBrowser.contentWindow.scrollX;
+      scrollY = gBrowser.contentWindow.scrollY;
+      isnot(scrollX, originalXPosition,
+            testName + " failed to scroll window horizontally");
+      isnot(scrollY, originalYPosition,
+            testName + " failed to scroll window vertically");
+
+      // Load a new document into the browser
+      var simple = "data:text/html,<html><head><title>test2</title></head>" +
+                     "<body>test2</body></html>";
+      gBrowser.loadURI(simple);
+      yield;
+
+
+      // Now go back in history. First page should not have been cached.
+      gBrowser.goBack();
+      yield;
+
+
+      // First uncacheable page will now be reloaded. Check scroll position
+      // restored, and form contents not
+      is(gBrowser.contentWindow.scrollX, scrollX, testName +
+         " horizontal axis scroll position not correctly restored");
+      is(gBrowser.contentWindow.scrollY, scrollY, testName +
+         " vertical axis scroll position not correctly restored");
+      var formValue = gBrowser.contentDocument.getElementById("inp").value;
+      isnot(formValue, text, testName + " form value incorrectly restored");
+
+    
+      // https no-cache
+      testName = "[nocache]";
+
+      // Load a page with a no-cache header
+      gBrowser.loadURI(nocacheURI);
+      yield;
+
+
+      // Now that the page has loaded, amend the form contents
+      form = gBrowser.contentDocument.getElementById("inp");
+      form.value = text;
+
+      // Attempt to scroll the page
+      originalXPosition = gBrowser.contentWindow.scrollX;
+      originalYPosition = gBrowser.contentWindow.scrollY;
+      scrollToX = gBrowser.contentWindow.scrollMaxX;
+      scrollToY = gBrowser.contentWindow.scrollMaxY;
+      gBrowser.contentWindow.scrollBy(scrollToX, scrollToY);
+      
+      // Save the scroll position for future comparison
+      scrollX = gBrowser.contentWindow.scrollX;
+      scrollY = gBrowser.contentWindow.scrollY;
+      isnot(scrollX, originalXPosition,
+            testName + " failed to scroll window horizontally");
+      isnot(scrollY, originalYPosition,
+            testName + " failed to scroll window vertically");
+
+      gBrowser.loadURI(simple);
+      yield;
+
+
+      // Now go back in history. First page should not have been cached.
+      gBrowser.goBack();
+      yield;
+
+
+      // First uncacheable page will now be reloaded. Check scroll position
+      // restored, and form contents not
+      is(gBrowser.contentWindow.scrollX, scrollX, testName +
+         " horizontal axis scroll position not correctly restored");
+      is(gBrowser.contentWindow.scrollY, scrollY, testName +
+         " vertical axis scroll position not correctly restored");
+      var formValue = gBrowser.contentDocument.getElementById("inp").value;
+      isnot(formValue, text, testName + " form value incorrectly restored");
+      
+      // nextTest has to be called from here, as no events are fired in this
+      // step
+      setTimeout(nextTest, 0);
+      yield;
+    }
+  ]]></script>
+
+  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/test_bug215405.xul
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+  href="chrome://mochikit/content/tests/SimpleTest/test.css"
+  type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=215405
+-->
+<window title="Mozilla Bug 215405"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Test for Bug 215405</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body  xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+   href="https://bugzilla.mozilla.org/show_bug.cgi?id=215405">Mozilla Bug 215405</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 215405 **/
+
+SimpleTest.waitForExplicitFinish();
+window.open("bug215405_window.xul", "bug215405",
+            "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1962,22 +1962,18 @@ DocumentViewerImpl::Hide(void)
   nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
 
   if (selPrivate && mSelectionListener) {
     selPrivate->RemoveSelectionListener(mSelectionListener);
   }
 
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
   if (docShell) {
-    PRBool saveLayoutState = PR_FALSE;
-    docShell->GetShouldSaveLayoutState(&saveLayoutState);
-    if (saveLayoutState) {
-      nsCOMPtr<nsILayoutHistoryState> layoutState;
-      mPresShell->CaptureHistoryState(getter_AddRefs(layoutState), PR_TRUE);
-    }
+    nsCOMPtr<nsILayoutHistoryState> layoutState;
+    mPresShell->CaptureHistoryState(getter_AddRefs(layoutState), PR_TRUE);
   }
 
   mPresShell->Destroy();
   // Clear weak refs
   mPresContext->SetContainer(nsnull);
   mPresContext->SetLinkHandler(nsnull);                             
 
   mPresShell     = nsnull;
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -1630,17 +1630,17 @@ nsFrameManager::RestoreFrameStateFor(nsI
                                      nsILayoutHistoryState* aState,
                                      nsIStatefulFrame::SpecialStateID aID)
 {
   if (!aFrame || !aState) {
     NS_WARNING("null frame or state");
     return;
   }
 
-  // Only capture state for stateful frames
+  // Only restore state for stateful frames
   nsIStatefulFrame* statefulFrame;
   CallQueryInterface(aFrame, &statefulFrame);
   if (!statefulFrame) {
     return;
   }
 
   // Generate the hash key the state was stored under
   // Exit early if we get empty key
--- a/layout/base/nsILayoutHistoryState.h
+++ b/layout/base/nsILayoutHistoryState.h
@@ -45,18 +45,18 @@
 #define _nsILayoutHistoryState_h
 
 #include "nsISupports.h"
 #include "nsStringFwd.h"
 
 class nsPresState;
 
 #define NS_ILAYOUTHISTORYSTATE_IID \
-{ 0x99003f0f, 0x7ade, 0x44a1, \
- { 0x81, 0x74, 0xe3, 0x6a, 0xa5, 0xbb, 0x6b, 0x10 } }
+{ 0x003919e2, 0x5e6b, 0x4d76, \
+ { 0xa9, 0x4f, 0xbc, 0x5d, 0x15, 0x5b, 0x1c, 0x67 } }
 
 class nsILayoutHistoryState : public nsISupports {
  public: 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILAYOUTHISTORYSTATE_IID)
 
   /**
    * Set |aState| as the state object for |aKey|.
    * This _transfers_ownership_ of |aState| to the LayoutHistoryState.
@@ -74,16 +74,22 @@ class nsILayoutHistoryState : public nsI
    * Remove the state object for |aKey|.
    */
   NS_IMETHOD RemoveState(const nsCString& aKey) = 0;
 
   /**
    * Check whether this history has any states in it
    */
   NS_IMETHOD_(PRBool) HasStates() const = 0;
+
+  /**
+   * Sets whether this history can contain only scroll position history
+   * or all possible history
+   */
+   NS_IMETHOD SetScrollPositionOnly(const PRBool aFlag) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsILayoutHistoryState,
                               NS_ILAYOUTHISTORYSTATE_IID)
 
 nsresult
 NS_NewLayoutHistoryState(nsILayoutHistoryState** aState);
 
--- a/layout/base/nsLayoutHistoryState.cpp
+++ b/layout/base/nsLayoutHistoryState.cpp
@@ -54,20 +54,22 @@ public:
 
   NS_DECL_ISUPPORTS
 
   // nsILayoutHistoryState
   NS_IMETHOD AddState(const nsCString& aKey, nsPresState* aState);
   NS_IMETHOD GetState(const nsCString& aKey, nsPresState** aState);
   NS_IMETHOD RemoveState(const nsCString& aKey);
   NS_IMETHOD_(PRBool) HasStates() const;
+  NS_IMETHOD SetScrollPositionOnly(const PRBool aFlag);
 
 
 private:
   ~nsLayoutHistoryState() {}
+  PRBool mScrollPositionOnly;
 
   nsClassHashtable<nsCStringHashKey,nsPresState> mStates;
 };
 
 
 nsresult
 NS_NewLayoutHistoryState(nsILayoutHistoryState** aState)
 {
@@ -90,36 +92,50 @@ NS_NewLayoutHistoryState(nsILayoutHistor
 
 NS_IMPL_ISUPPORTS2(nsLayoutHistoryState,
                    nsILayoutHistoryState,
                    nsISupportsWeakReference)
 
 nsresult
 nsLayoutHistoryState::Init()
 {
+  mScrollPositionOnly = PR_FALSE;
   return mStates.Init() ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsLayoutHistoryState::AddState(const nsCString& aStateKey, nsPresState* aState)
 {
   return mStates.Put(aStateKey, aState) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 NS_IMETHODIMP
 nsLayoutHistoryState::GetState(const nsCString& aKey, nsPresState** aState)
 {
-  mStates.Get(aKey, aState);
+  PRBool entryExists = mStates.Get(aKey, aState);
+
+  if (entryExists && mScrollPositionOnly) {
+    // Ensure any state that shouldn't be restored is removed
+    (*aState)->ClearNonScrollState();
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLayoutHistoryState::RemoveState(const nsCString& aKey)
 {
   mStates.Remove(aKey);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(PRBool)
 nsLayoutHistoryState::HasStates() const
 {
   return mStates.Count() != 0;
 }
+
+NS_IMETHODIMP
+nsLayoutHistoryState::SetScrollPositionOnly(const PRBool aFlag)
+{
+  mScrollPositionOnly = aFlag;
+  return NS_OK;
+}
--- a/layout/base/nsPresState.cpp
+++ b/layout/base/nsPresState.cpp
@@ -103,18 +103,20 @@ nsPresState::RemoveStateProperty(const n
   return NS_OK;
 }
 
 nsresult
 nsPresState::GetStatePropertyAsSupports(const nsAString& aName,
                                         nsISupports** aResult)
 {
   // Retrieve from hashtable.
-  mPropertyTable.Get(aName, aResult);
-  return NS_OK;
+  if (mPropertyTable.Get(aName, aResult))
+    return NS_STATE_PROPERTY_EXISTS;
+
+  return NS_STATE_PROPERTY_NOT_THERE;
 }
 
 nsresult
 nsPresState::SetStatePropertyAsSupports(const nsAString& aName,
                                         nsISupports* aValue)
 {
   mPropertyTable.Put(aName, aValue);
   return NS_OK;
@@ -139,16 +141,22 @@ nsPresState::GetScrollState()
   if (!mScrollState) {
     nsRect empty(0,0,0,0);
     return empty;  
   }
 
   return *mScrollState;
 }
 
+void
+nsPresState::ClearNonScrollState()
+{
+  mPropertyTable.Clear();
+}
+
 nsresult
 NS_NewPresState(nsPresState** aState)
 {
   nsPresState *state;
 
   *aState = nsnull;
   state = new nsPresState();
   if (!state)
--- a/layout/base/nsPresState.h
+++ b/layout/base/nsPresState.h
@@ -66,17 +66,19 @@ public:
 
   NS_HIDDEN_(nsresult) SetStateProperty(const nsAString& aProperty,
                                         const nsAString& aValue);
 
   NS_HIDDEN_(nsresult) RemoveStateProperty(const nsAString& aProperty);
 
   NS_HIDDEN_(nsresult) SetScrollState(const nsRect& aState);
 
-  nsRect GetScrollState();
+  nsRect               GetScrollState();
+
+  NS_HIDDEN_(void)     ClearNonScrollState();
 
 // MEMBER VARIABLES
 protected:
   nsInterfaceHashtable<nsStringHashKey,nsISupports> mPropertyTable;
   nsAutoPtr<nsRect> mScrollState;
 };
 
 NS_HIDDEN_(nsresult) NS_NewPresState(nsPresState **aState);
--- a/layout/forms/nsIsIndexFrame.cpp
+++ b/layout/forms/nsIsIndexFrame.cpp
@@ -71,16 +71,17 @@
 #include "nsIDOMKeyEvent.h"
 #include "nsIFormControlFrame.h"
 #include "nsINodeInfo.h"
 #include "nsIDOMEventTarget.h"
 #include "nsContentCID.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
+#include "nsLayoutErrors.h"
 
 nsIFrame*
 NS_NewIsIndexFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsIsIndexFrame(aContext);
 }
 
 nsIsIndexFrame::nsIsIndexFrame(nsStyleContext* aContext) :
@@ -547,11 +548,12 @@ nsIsIndexFrame::RestoreState(nsPresState
 {
   NS_ENSURE_ARG_POINTER(aState);
 
   // Set the value to the stored state.
   nsAutoString stateString;
   nsresult res = aState->GetStateProperty(NS_LITERAL_STRING("value"), stateString);
   NS_ENSURE_SUCCESS(res, res);
 
-  SetInputValue(stateString);
+  if (res == NS_STATE_PROPERTY_EXISTS)
+    SetInputValue(stateString);
   return NS_OK;
 }