[Bug 21957] New: Adjust the Reset the form owner algorithm to match reality

https://www.w3.org/Bugs/Public/show_bug.cgi?id=21957

            Bug ID: 21957
           Summary: Adjust the Reset the form owner algorithm to match
                    reality
    Classification: Unclassified
           Product: HTML WG
           Version: unspecified
          Hardware: PC
                OS: Windows NT
            Status: NEW
          Severity: normal
          Priority: P2
         Component: HTML5 spec
          Assignee: dave.null@w3.org
          Reporter: travil@microsoft.com
        QA Contact: public-html-bugzilla@w3.org
                CC: mike@w3.org, public-html-admin@w3.org,
                    public-html-wg-issue-tracking@w3.org

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)

-- 
You are receiving this mail because:
You are the QA Contact for the bug.

Received on Tuesday, 7 May 2013 21:48:40 UTC