This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 22183 - Adjust the Reset the form owner algorithm to match reality
Summary: Adjust the Reset the form owner algorithm to match reality
Status: RESOLVED WONTFIX
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: PC Windows NT
: P2 normal
Target Milestone: Unsorted
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL:
Whiteboard:
Keywords:
Depends on: 21957
Blocks:
  Show dependency treegraph
 
Reported: 2013-05-28 21:28 UTC by Ian 'Hixie' Hickson
Modified: 2014-03-05 00:44 UTC (History)
13 users (show)

See Also:


Attachments

Description Ian 'Hixie' Hickson 2013-05-28 21:28:35 UTC
(clone of bug 21957, Travis writing:)

We recently came across a site bug [1] that is broken in IE10 and Firefox, but does work in Chrome and older versions of IE, and the root cause is due to a subtle variation on the reset the form owner algorithm that exists in Chrome.

Because the site works in Chrome and old versions of IE, we believe it is appropriate to fix the HTML5 spec to match reality.

Details on what we observed about Chrome's behavior follow

--------------

The site's behavior boils down to the following problem where the submit button does not end up associated with the form:

<div id="b"></div>
 <script>
  document.getElementById('b').innerHTML =
     '<table><form id="d" action="..." method="post"><tr><td><input id="e" type="submit" name="Submit"></td></tr></form>';
 </script>
 <script>
   alert(document.getElementById('e').form.id);
</script>

In IE10/Firefox, the input element's form is null because:
* Per step 2 of the reset the form owner algorithm (below), the form owner is cleared
* Step 3 doesn't apply in this scenario
* There is no ancestor form after foster-parenting in step 4
* In step 5, the form owner is left unassociated

However, in Chrome, the element's form is "d".

===RESET the form owner algorithm (As currently defined)===
1. If the element's form owner is not null, and the element's form content attribute is not present, and the element's form owner is its nearest form element ancestor after the change to the ancestor chain, then do nothing, and abort these steps.
2. Let the element's form owner be null.
3. If the element has a form content attribute and is itself in a Document, then run these substeps:
3.1. If the first element in the Document to have an ID that is case-sensitively equal to the element's form content attribute's value is a form element, then associate the form-associated element with that form element.
3.2. Abort the "reset the form owner" steps.
4. Otherwise, if the form-associated element in question has an ancestor form element, then associate the form-associated element with the nearest such ancestor form element.
5. Otherwise, the element is left unassociated.
=================

From what we've been able to deduce from various test cases, Chrome actually runs a variation of this algorithm:

===RESET the form owner algorithm (tweaked for Chrome compat)===
1. If the element's form owner is not null, and the element's form content attribute is not present, and the element's form owner is its nearest form element ancestor after the change to the ancestor chain, then do nothing, and abort these steps.
2. If the element has a form content attribute and is itself in a Document, then run these substeps:
2.1. If the first element in the Document to have an ID that is case-sensitively equal to the element's form content attribute's value is a form element, then associate the form-associated element with that form element.
2.2. Abort the "reset the form owner" steps.
3. Otherwise, if the form-associated element in question has an ancestor form element, then associate the form-associated element with the nearest such ancestor form element.
4. Otherwise, if the element and its existing form owner belong to the same home subtree, then do nothing, and abort these steps.
5. Otherwise, let the element's form owner be null.
=================

And Chome honors the existing removal condition, but applies these conditions on _any_ removal, not just from a document (for example, it can be observed in a removal from an orphaned fragment into another fragment):
"When an element is removed **from a Document** resulting in a form-associated element and its form owner (if any) no longer being in the same home subtree, then the user agent must reset the form owner of that form-associated element." (** annotation added for emphasis)

These changes cause the form owner association to no longer be unconditionally cleared as was previously done in step 2 of the original algorithm, but rather preserves the association with the previous form owner so long as you always move a common ancestor of the input and its form owner and no new appropriate form owner is found.

---------

Here's some supporting testing and commentary.

The following only preserve the form owner in Chrome:

 <!DOCTYPE html>
  <!--<form id="a">-->
  <div id="b"></div>
  <!--</form>-->
 <script>
  var b = document.getElementById('b');
  var parent = b.parentNode;
  // === Remove b from the document to check if Chrome 
  // uses a common home subtree, or the document as the basis
  // for perserving the form owner
  parent.removeChild(b);
  // === Run the foster-parent algorithm, then copy the contents 
  // into b (not connected to the document) Per current
  // spec, the form owner should be null (no suitable ancestor)
  // Chrome maintains the association...
  b.innerHTML =
     '<table><form id="d" action="..." method="post"><tr><td><input id="e" type="submit" name="Submit"></td></tr></form>';
  // === Chome's behavior is not limited to the HTML fragment 
  // parsing algorithm, as the following move into the document
  // continues to preserve the form owner association...
  parent.appendChild(b);
  // === Defeat an optimization in Chrome (no-op append in 
  // the second step)
  b.parentNode.appendChild(document.createTextNode('test'));
  // === Intra-document moves continue to preserve the association...
  b.parentNode.appendChild(b);
 </script>
 <script>
   // === Chrome alerts "d", IE10/Firefox script error (null)
   alert(document.querySelector('#e').form.id);
</script>

All browser currently drop the form owner association in this case (and it should stay that way):

 <!DOCTYPE html>
  <!--<form id="a">-->
  <div id="b"></div>
  <!--</form>-->
 <script>
  var b = document.getElementById('b');
  var parent = b.parentNode;
  parent.removeChild(b);
  // === So far, same as the last test, Chrome maintains the
  // association...
  b.innerHTML =
     '<table><form id="d" action="..." method="post"><tr><td><input id="e" type="submit" name="Submit"></td></tr></form>';
  // === Now, Chrome breaks the association because, as part
  // of the move, the form element and its form owner did 
  // not share a common home subtree, and no other suitable
  // form owner was found
  parent.appendChild(b.querySelector('tbody'));
 </script>
 <script>
   // === All browsers have a script error here (null)
   alert(document.querySelector('#e').form.id);
</script>

All browsers currently get the right form owner association in the example case described in the current spec:

 <!DOCTYPE html>
 <form id="a">
  <div id="b"></div>
 </form>
 <script>
  document.getElementById('b').innerHTML =
     '<table><tr><td><form id="c"><input id="d"></table>' +
     '<input id="e">';
 </script>
 <script>
  // === Ultimately, form "a" is re-associated as "e"'s
  // form owner because it exists in the input's 
  // ancestry chain as illustrated in the spec. If 
  // the form "a" is commented out, then Chrome still does 
  // not associate "e" with "c" because there is no common
  // home subtree in the innerHTML injection to "b" between
  // the two (the table element and "e" are siblings). 
  // If, however, the form "a" is commented out AND a div
  // element wrapper is added around the innerHTML-injected 
  // string, then Chrome retains "c" as the form owner for 
  // both "d" and "e", while IE10/Firefox/HTML5 spec (currently) 
  // make this a script error (null)
  alert(document.getElementById('e').form.id);
</script>

-----------

[1] http://sc.jz123.cn/tuku/ai/201161/TK21103.shtml (comment button doesn't work)
Comment 1 Ian 'Hixie' Hickson 2013-06-12 17:35:17 UTC
The suggestion as given wouldn't work, because it would mean that:

   <form id=a>
    <input form=a id=b>
   </form>
   <script>
    document.getElementById('b').form = 'c';
   </script>

...would leave the input associated.


I'm a little concerned about the last example (the one from the spec today, which the browser agree on). Why would it keep working with the change? The subtree root element would change from a DocumentFragment to a Document, but at no point are the elements without a common home subtree, so why would it reset?

Are there any more pages that are broken by this? I couldn't even get the form to show up on IE9. I'm reluctant to make this change unless it's more widespread than just one page. (You need a rather weird set of events to trigger this -- innerHTML to insert a form and a control, where they are in an invalid <table> hierarchy.)

Would it not be better just to fix Safari/Chrome?
Comment 2 Travis Leithead [MSFT] 2013-06-19 01:50:38 UTC
I'm going to be out on vacation for a few weeks, but I discussed this with the opener on the IE compat side. To our knowledge, this one site is the only instance we have seen that depends on the particular Chrome behavior that was cited in this bug. Having said that, it's been our experience that when we find a bug on our initial ring of testing the top sites, it means it's just the tip of the iceberg. Since Chrome has nothing to loose by not changing to support the spec as-is, we're just worried that this behavior will come back to bit us later on more sites.

So, if you want to punt it, then I suppose that's OK, but I'm only concerned that this will bite us later on.
Comment 3 Ian 'Hixie' Hickson 2013-07-02 22:00:44 UTC
Filed a bug to get Chrome fixed, we'll see if they are able to.
https://code.google.com/p/chromium/issues/detail?id=256797
Comment 4 Kent Tamura 2014-01-22 23:21:47 UTC
(In reply to Ian 'Hixie' Hickson from comment #3)
> Filed a bug to get Chrome fixed, we'll see if they are able to.
> https://code.google.com/p/chromium/issues/detail?id=256797

We fixed it in Blink.
http://src.chromium.org/viewvc/blink?view=revision&revision=165540

WebKit still have this issue.
Comment 5 Ian 'Hixie' Hickson 2014-02-05 18:42:47 UTC
So the spec's algorithm has changed a bit since this bug was filed, but I think we have convergence both from browsers and the spec here, and it seems compatible with legacy content, so I think this bug should probably be WONTFIX at this point.

Travis, do you agree?
Comment 6 Travis Leithead [MSFT] 2014-02-05 18:51:31 UTC
(In reply to Ian 'Hixie' Hickson from comment #5)
> So the spec's algorithm has changed a bit since this bug was filed, but I
> think we have convergence both from browsers and the spec here, and it seems
> compatible with legacy content, so I think this bug should probably be
> WONTFIX at this point.
> 
> Travis, do you agree?

Yep, this works for me. Good to see the convergence here ;-)
Comment 7 Ian 'Hixie' Hickson 2014-02-05 20:51:54 UTC
Thanks!
Comment 8 David Kilzer 2014-03-05 00:44:26 UTC
(In reply to Kent Tamura from comment #4)
> (In reply to Ian 'Hixie' Hickson from comment #3)
> > Filed a bug to get Chrome fixed, we'll see if they are able to.
> > https://code.google.com/p/chromium/issues/detail?id=256797
> 
> We fixed it in Blink.
> http://src.chromium.org/viewvc/blink?view=revision&revision=165540
> 
> WebKit still have this issue.

WebKit bug:  https://bugs.webkit.org/show_bug.cgi?id=129716