Open Bug 654072 Opened 13 years ago Updated 20 hours ago

form input state (including disabled state and other properties) are cached across reloads and history navigation

Categories

(Core :: DOM: Forms, defect, P3)

defect

Tracking

()

People

(Reporter: aardmaat, Unassigned)

References

(Blocks 2 open bugs, )

Details

(Keywords: testcase)

Attachments

(3 files)

User-Agent:       Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0a2) Gecko/20110501 Firefox/5.0a2
Build Identifier: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0a2) Gecko/20110501 Firefox/5.0a2

on a forum I helped with a script
when doing that it appeared that firefox was the only browser that didn't behave as expected with this script:

<html>
<head>
<script>
for(var i=0;i<20;i++)
setTimeout("foo('"+i+"')",(20-i)*2000);
function foo(n)
{
	document.getElementById('btn').value=n;
	if(n==0)
  {
	document.getElementById('btn').value='Download';
	document.getElementById('btn').disabled=false;
  }
}
</script>
</head>
<body bgcolor="#00CCCC">
<input style='width:150px' id='btn' type='button' value='20' disabled='disabled' onclick='window.location="mijndownloadbestand.rar"'>
</body>
</html>

(you might want to change the timer for testing, 20 seconds is long!)

after 20 seconds the button for downloading is enabled, that's just fine,
when you refresh though, the button starts counting again but isn't disabled at the beginning. all other tested browsers (opera, chrome, ie9) do disable the button after refreshing the page.
this flaw would cause developers to make patches for firefox.

Reproducible: Always

Steps to Reproduce:
1.make a html file with the script
2.open it in firefox and an other browser
3.waith till the timer enables the button
4.refresh the page
5.firefox doesn't disable the button, other browser does

Actual Results:  
firefox's behaviour was out of sync with the other browsers

Expected Results:  
it should have disabled the button again for 20 seconds

this also happened with the firefox browser used by the person who needed help (because of this bug) with the script. I don't know what version he/she uses but it probably isn't aurora (so the bug is probably in more different versions of firefox)
Attached file example of the code
an example of the script
Attachment #529443 - Attachment mime type: text/plain → text/html
I confirm the issue on Gecko 1.9.1, 1.9.2 and trunk.
I believe there is a dup somewhere.
Status: UNCONFIRMED → NEW
Ever confirmed: true
OS: Windows 7 → All
Hardware: x86 → All
Whiteboard: DUPEME
Version: unspecified → Trunk
Component: Layout: Form Controls → DOM: Core & HTML
QA Contact: layout.form-controls → general
just realized it isn't css what I'm talking about, it's just html attribute,
sorry for my bad description
Modified disabled state is one of the form states that we preserve across history navigations and reloads, yes.  That's done quite explicitly.
what's the reason for that?
shouldn't things like that be the same across al browsers?
> what's the reason for that?

I believe the idea was that a non-forced reload should be preserving all user-visible form state as much as possible (since it's a request to show again exactly what you're seeing, not to rerequest it all from the server).

> shouldn't things like that be the same across al browsers?

History operations are decidedly not the same across all browsers, and likely never will be....
Summary: if css is changed with javascript, the normal css doesn't return after refreshing the page → if disabled state is changed with javascript, the normal state doesn't return after refreshing the page
I think this is a bug and needs to be fixed. It's not consistent with other browsers. And if you ask me, the developers should take care of these things, not the browser itself. E.g. run validations on DOM load. There is just too many cases where this is not the desired behavior. Filling in values into input fields is ok, but not this. And it works "properly" in other major browsers.
Blocks: 592665
Blocks: 516920
Here's an old (resolved/invalid) bug about this: bug 293733.

from bz's comment 7 there:
> > why is disabled state of form elements persisted? 
> 
> I (or someone else) would have to go back and read the original bugs on
> that...  You're welcome to if you want to, of course.  It's about rock
> bottom low priority for me.
> 
> I wouldn't be opposed to changing the behavior if someone comes up with a
> consistent description of what form state restoration _should_ act like. 
> I'm opposed to random back-and-forth tweaking, though.

and for those interested in digging up the previous changes made in this area, the code is here: http://mxr.mozilla.org/mozilla-central/search?string=::SaveState
Keywords: testcase
Whiteboard: DUPEME
I'm throwing in my vote to fix this... I made a demo with a disabled button being added after the page loads: http://fiddle.jshell.net/Mottie/Adz9L/1/show/

Keep hitting the reload button without holding down the shift key and watch what happens.
(In reply to Boris Zbarsky (:bz) from comment #6)
> I believe the idea was that a non-forced reload should be preserving all
> user-visible form state as much as possible (since it's a request to show
> again exactly what you're seeing, not to rerequest it all from the server).

I agree that this is bad.  Form state should attempt to preserve *user-entered* information.  The disabled state of form elements isn't stuff the user typed in, it's stuff the page and scripts have set.  Preserving that does nothing but break pages; the disabled state is no more "form data" than class names and other arbitrary bits of DOM state.

I've hit this problem myself on EasyNews's search (a subscription service; can't usefully give a link), which disables the submit button when the form is submitted.  If I then reload the page before the submit completes, the form comes back unsubmittable.
You can't win there: disabled state is often set based on information the user entered (e.g. selecting one of a set of radios can enable/disable other controls), so restoring values but not disabled state will fix some pages but break others....

What we really need is some better way of restoring form data that informs the page about what exactly was restored or something.  Maybe we can make our restoration look like a user interacting with the page?
(In reply to Boris Zbarsky (:bz) from comment #14)
> You can't win there: disabled state is often set based on information the
> user entered (e.g. selecting one of a set of radios can enable/disable other
> controls), so restoring values but not disabled state will fix some pages
> but break others....

I think not restoring one select bit of DOM state as if it's form state is a step up, though.  Selecting that same set of radios might do any number of things, like hiding/showing arbitrary elements ("your password is weak"), or creating entirely new ones (Google Instant).

> What we really need is some better way of restoring form data that informs
> the page about what exactly was restored or something.  Maybe we can make
> our restoration look like a user interacting with the page?

That might be risky, since the page may react to the "user interactions" unexpectedly.  For example, if a user's authentication has expired, trying to enter data into a form might pop up an authentication overlay, when the page only meant  to do upon a real user interaction.
(In reply to Boris Zbarsky (:bz) from comment #6)
> > what's the reason for that?
> 
> I believe the idea was that a non-forced reload should be preserving all
> user-visible form state as much as possible (since it's a request to show
> again exactly what you're seeing, not to rerequest it all from the server).

A non-forced reload could be:
 1. A request to see again what you see now (as described above).
 2. A request to see what you saw when you first visited the page.
 3. A request to see what is on the server right now.

I would suggest that #1 is the least likely of these in the mind of the typical user and that #3 is beyond the technical understanding of the typical user (and already has its own, separate, invocation method), leaving #2 as the most useful implementation.

But the current implementation creates a 4th option:
 4. A request to see what you saw when you first visited the page, except for certain things, where 'some things' is defined in terms the typical user does not understand.  Moreover, if the 'some things' are logically related to other things, the user may see two kinds of data, that which is the same as when the page was first viewed and that which is not, in a relationship that no longer is consistent.

Example: http://stl.beallsprings.org/conservation%20easement.html

The checkbox toggles the display between two versions by modifying CSS.  The box is checked upon initial loading, which corresponds to what is displayed elsewhere on the page.  If you UNcheck the box, CSS is modified and the user sees a different view of the data (in this case a filtered view and a different background color).  The view of the data and the checkbox setting are in sync, as expected.  This synchronization remains as you check and uncheck the checkbox.

Now uncheck the checkbox, then reload the page.  The data has been restored to its original view, i.e. with annotations, but the checkbox remains unchecked, i.e. the two are now out of sync and the user is confused.

In this case, the problem results because the initial state of the checkbox, which is created by JavaScript with a specification of 'checked', is not restored when the page is reloaded.  This is apparently a direct result of the philosophy described above.

My symptoms are a possible dup of 693927, which also seems to be related to this philosophy.

A workaround, which I have implemented on page http://stl.beallsprings.org/Timeline.html, is to explicitly restore the initial state of the checkboxes using additional JavaScript driven by body onload=.  This is not necessary on any other browser that I have tested.

I continue to believe that interpretation #2 more closely matches the typical user expectation and thus is the best choice.
Chris, the most common reason for non-forced reloads are scripts running location.reload() and such, not users using the reload button.... and pages do it in all sorts of situations when they really mean #1.

I agree that the current situation is suboptimal; I'm just not sure how to actually make it better as opposed to trading off one set of problems (primarily biting website authors) for another (primarily biting users).
I'm disappointed that this hasn't been changed to match the behavior of every other browser.

Maybe I'm not seeing the big picture here, but how is this issue biting the users? The button itself says "reload the current page" not "refresh the current page". When would the user want to refresh a page? Why not make the form reset button do this? I guess we have to assume there are users with many different levels of experience, but I think they would be even more confused when the experience isn't consistent across different browsers.

Please don't make Firefox into the IE6 of browsers.
Comment 16 suggests not restoring form state at all.  How would that bite users?  Take a guess....
And for the rest, see comment 14.  Maybe pages are now expecting the other behavior and coding to it explicitly, in which case I agree that we should just stop saving/restoring disabled state.

Jonas, thoughts?
(In reply to Boris Zbarsky (:bz) from comment #19)
> Comment 16 suggests not restoring form state at all.  How would that bite
> users?  Take a guess....

Boris,

My guesser must not be working well.  How WOULD it impact users?  More specifically, how would it impact them if non-forced reload worked as it does on other browsers, but Back (i.e. the history) worked as it does today.  I see no logical reason for these things to be functionally linked, as they apparently are today.

I noticed something else.  I have browser.formfill.enable set to false.  It's one of the first things I do when setting up any browser.  I know many people like that capability, but I'm not one of them.  So why is MY checkbox state being 'preserved' when I reload a page?  (Note that this is a logical trap; if we were to agree that formfill.enable should alter the behavior of a non-forced reload, we'd have added even more complexity for the developer (or more confusion on the part of a user who encounters a page that didn't account for the added complexity...))

Which raises an OT question: Can a JavaScript developer dynamically determine the state of FIrefox options such as browser.formfill.enable?
> More specifically, how would it impact them if non-forced reload worked as it does on
> other browsers,

Other browsers preserve various bits of form control state on reload in various cases, last I tested (as well as scroll position and such).  They're not consistent with each other or with us, of course.  Also, web pages make reload() calls based on browser sniffing....

> So why is MY checkbox state being 'preserved' when I reload a page? 

Because that's not autofill.  Autofill is filling in controls when the page first loads based on previous values you put into them some other time when you visited the page.
firefox is "preserving" disabled="disabled" on inputs that _were not disabled_ prior to reload

<input id="foo1">
<input id="foo2" disabled="disabled">
<input id="foo3">

note, i have not determined the order of how this happens, so don't infer.  the order probably has a derterministic affect, but i haven't figured it out yet.

on reload, this happens:

<input id="foo1">
<input id="foo2" disabled="disabled">
<input id="foo3" disabled="disabled">

why is firefox applying attributes to elements that didn't previously have them?

reference page:  https://dragonstone-mill.com/pcf.html
(use firebug on my page and you'll see plenty of debug about wrongly applied disabled attribute)
That sounds like a different issue.  You may want to create a minimal test case and open a new ticket.
(In reply to Chris Beall from comment #21)
> My guesser must not be working well.  How WOULD it impact users?  More
> specifically, how would it impact them if non-forced reload worked as it
> does on other browsers, but Back (i.e. the history) worked as it does today.

Belatedly, I find it annoying in other browsers when I reload a form with data in it, and all of the fields get cleared on me.

Of course, if I have significant data then I always make a copy before refreshing even in Firefox, since it doesn't actually always work.  Since it's a dangerous, brittle feature--it's easy to get used to it, and then lose data when it doesn't happen--I think it should never have been introduced in the first place, but removing it now would probably cause riots.

(In reply to Boris Zbarsky (:bz) [In and out Aug 1 - 10, out Aug 11-20] from comment #14)
> You can't win there: disabled state is often set based on information the
> user entered (e.g. selecting one of a set of radios can enable/disable other
> controls), so restoring values but not disabled state will fix some pages
> but break others....

That's true, but this is a game of whack-a-mole that you can't win: sites often do many other things on user entry that can't possibly be restored, such as hiding and displaying elements and all kinds of style/class changes.  (Of course, this stuff is far more common today then it was when this feature was first introduced.)

It might simply break too many sites to be doable, but I really think this should only preserve direct user input, and leave it to sites to apply other changes during onload.
If you persist anything which is not directly changeable by the user, it will probably break the page:

 * If you refresh a page and a text field value is prefilled, user can change the value and carry on.
 * If you refresh a page and a text field is disabled, user can not do anything about it...

> It might simply break too many sites to be doable...

During my web developer and web user lifetime I did not come across any site which might rely on this feature (but I understand this is kind of subjective view).
I think what Pavel Horal said makes a lot of sense. One thing is filling text boxes, a whole other thing is disabling fields. The other problem is that no other browser exhibits this behavior, only Firefox.
We've encountered this misfeature over at Bootstrap (see https://github.com/twbs/bootstrap/issues/793 ) and ended up having to add a warning box to our docs specifically covering this ( http://getbootstrap.com/javascript/#buttons-usage ).
I am disappointed that Firefox required us to do that.
Severity: minor → normal
I'll add my 2 cents:  This is a BUG, not a feature.  It should be fixed.
Blocks: 1230801
There are lots of good points for either side of this debate but as it requires a work-around by the developer to fix it and not wanting the button to re-enable would seem to be an edge case I would label this as undesired behaviour and would also say it should be fixed.

Also, Bootstrap's suggestion of using autocomplete="off" as a work-around does not appear to work for me.
This is my preferred solution:

1. Don't restore states of form fields that can't be changed by user directly (e.g. disabled).

2. Restore later (e.g. after DOMContentLoaded), and fire "change" events on restored fields to simulate user inputs, so that the page can responed to the restoration and maintain the consistancy of the page. This should address https://bugzilla.mozilla.org/show_bug.cgi?id=654072#c14 . The tricky thing is that some pages may expect users to fill form fields in perticular order, e.g. reveal more fields according to previous inputs. Restoring in document order is probably OK for most cases.

3. Provide a way to opt-out form fields restoration. May be something like history.scrollRestoration="manual" ( https://developer.mozilla.org/zh-CN/docs/Web/API/History ). This needs a spec update.
(In reply to Duan Yao from comment #34)
> This is my preferred solution:
> 
> 1. Don't restore states of form fields that can't be changed by user
> directly (e.g. disabled).

Exactly. It was always a bad idea to restore disabled states on reload (or return visits). Restore user input only. Any states that change in response to user input are to be restored by the script, which will have every chance to do that as on load. Meddling by the browser in this area is just plain misguided.

> 
> 2. Restore later (e.g. after DOMContentLoaded), and fire "change" events on
> restored fields to simulate user inputs, so that the page can responed to
> the restoration and maintain the consistancy of the page.

No. This is just more "magical" meddling by the browser. Any script that responds to "change" events by changing disabled states should always do the same on load (or DOMContentLoaded) as there's always a chance that user data is persisted. This is not a new concept for browsers and they should not try to force the issue by firing bogus change events, which are strictly for indicating *user* changes as they occur (not when they are persisted between page loads).

> This should
> address https://bugzilla.mozilla.org/show_bug.cgi?id=654072#c14 . The tricky
> thing is that some pages may expect users to fill form fields in perticular
> order, e.g. reveal more fields according to previous inputs. Restoring in
> document order is probably OK for most cases.

There's nothing tricky about it and never has been. Scripts that hide or show elements in response to user input are responsible for synchronizing these states on load, just as when they disable elements in response to same. There's simply nothing for the browser to solve here, certainly not after two decades.

> 
> 3. Provide a way to opt-out form fields restoration. May be something like
> history.scrollRestoration="manual" (
> https://developer.mozilla.org/zh-CN/docs/Web/API/History ). This needs a
> spec update.

As far as persisting user *input* on revisiting a page? Fine, but realize that most users will never turn that off. Imagine the majority will never even find such a setting.

In any event, a properly designed script should never run into these issues as it would simply override all such meddling on load (or DOMContentLoaded). Of course, that depends on when such meddling occurs. Please do everyone a favor and make that a moot point.

Realize that no other browsers have such issues and properly designed scripts have managed for two decades without help in this area. Trying to prop up improperly designed scripts is just going to create more confusion and possibly trip up scripts that are trying to do their job properly.

HTH
This bug is the root cause of an issue I was having using Redmine. This bug caused the local form to be populated with out-of-date data from the cache, which upon submission would overwrite the correct data on the server! This caused several data quality issues for our Redmine site.

This was difficult to identify as the root cause. If this bug was fixed that would be really great.
I would also like to chime in - I work with bmbouter and have also been bitten by this issue on multiple occasions.  Essentially, if we have an issue open in a tab and it gets edited by someone else, and then we try to edit it ourselves (even after refreshing the page with F5), Firefox will attempt to use the local form state instead of the server-provided form state, thus unintentionally overwriting all the other person's changes when we save the issue.  This has caused us to accidentally put issues in the wrong state on multiple occasions.  

e.g. someone changes the priority from Normal to Urgent, I edit the issue, it gets accidentally reverted back to Normal priority because Firefox is trying to use its own copy of the form state.  

As you can imagine, that is very problematic behavior.

(In reply to Boris Zbarsky from comment #17)

> I agree that the current situation is suboptimal; I'm just not sure how to
> actually make it better as opposed to trading off one set of problems
> (primarily biting website authors) for another (primarily biting users).

At least in our case, the problem is biting both users and website authors.  We reported the issue initially to our Redmine hosting company, who is now needing to write a Firefox-specific patch for the issue.

(In reply to Glenn Maynard from comment #26)

>  I really think this should only preserve direct user input, and leave it to sites to apply other changes during onload.

+1
(In reply to dalley from comment #39)
> e.g. someone changes the priority from Normal to Urgent, I edit the issue,
> it gets accidentally reverted back to Normal priority because Firefox is
> trying to use its own copy of the form state.

I think this issue with Redmine is more likely caused by https://bugzilla.mozilla.org/show_bug.cgi?id=1279253 where an unchanged select box not refreshed when F5 is pressed.
Yes, comment 38 and 39 are not related to this bug at all, because they're not dealing with restoration of disbled state.
My apologies, that does seem likely.
Is this bug stil actual. After 7 years?
aartmaat's test case confirmed in 58.0.1.
Priority: -- → P3
This problem is gone if you reload the page in 61.0.1 but if you click download then go back it's active.

I notice strange behavior of disabled in FF 66.0.2.
If the button becomes enabled, its state is saved after refresh.
Disabled state of button resets.

Flags: webcompat?

See bug 1547409. Moving webcompat whiteboard tags to project flags.

Webcompat Priority: --- → ?
Whiteboard: [webcompat-revisit]
Webcompat Priority: ? → revisit
Whiteboard: [webcompat-revisit]
Flags: webcompat?
Flags: needinfo?(jstutte)

Wow, 11 years old. Just been bitten by this from a bug report from our users.

The auto-restoration of form fields basically BREAKS AJAX form submission flow - on reload the old form values will be restored. This can cause duplicate page submissions from users.

Generally any time you prevent the form from being submitted, Firefox will keep the old form values and restore on reload. There's nothing you can do about that, except add autocomplete=off to your form fields which sucks because it fixes this problem and breaks another useful feature.

Firefox is the odd one out here. Chrome doesn't auto restore, nor does Safari. Why is Firefox having such an opinionated way of handling this that isn't even mentioned anywhere in the spec?

Would be great to have more eyes on this.

===

Example code:

<form method="POST">
	<textarea name="test"></textarea>
	<button name="submit" value="submit" type="submit">
		Submit
	</button>
</form>

<script>
  document.querySelector('form').addEventListener('submit', function(e) {
     e.preventDefault(); // Firefox doesn't care. It will still persist the form values.

     // Handling the form by ajax. Firefox doesn't see this and will still keep old form data on reload.
     fetch(e.target.action, { method: e.target.method, body: new FormData(e.target) });
  });
</script>
Component: DOM: Core & HTML → DOM: Forms

Anne, is any of this specced these days? Clearly, 10 years ago it wasn't... (cf comment 6)

Any alignment with other browsers probably needs some kind of spec. Also, my understanding is that chromium recently started adding a feature similar to bfcache, which I imagine will be related (but I'm not sure how they treat form fields in those circumstances).

Flags: needinfo?(annevk)
Summary: if disabled state is changed with javascript, the normal state doesn't return after refreshing the page → form input state (including disabled state and other properties) are cached across reloads and history navigation

There is nothing concrete, but your ping (and that of holegary) was rather timely. https://github.com/whatwg/html/issues/6853 has discussion about this behavior and Chrome is also making changes in this area. I asked Chrome if they looked at disabled state and whether there should be an event for web developers.

Edit: discussion moved to https://github.com/whatwg/html/issues/7292.

Flags: needinfo?(annevk)
Severity: normal → S3
Webcompat Priority: revisit → ---

Any workaround for this bug without JS?

autocomplete="off"?

(In reply to Emilio Cobos Álvarez (:emilio) from comment #58)

autocomplete="off"?

It is forbidden by validator.w3.org for radiobuttons. I use radiobuttons to switch tabs. radiobuttons have no <form/> parent thus I cannot set it on it.

I found workaround for tabs made on radiobuttons.

<input type="radio" name="tab-$rnd" id="tab1" checked="checked"/>
<input type="radio" name="tab-$rnd" id="tab2"/>

The $rnd is random number or epoch in milliseconds. The state of tabs is no longer remembered after refresh.

It is sad, we report the bugs and no one does remove them for 12 years. This error does not appear to be in a blocking state.

You need to log in before you can comment on or make changes to this bug.