<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[164475] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/164475">164475</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2014-02-20 22:40:05 -0800 (Thu, 20 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>m_ancestorDisabledState should never be unknown
https://bugs.webkit.org/show_bug.cgi?id=129084

Reviewed by Benjamin Poulain.

Source/WebCore: 

In order to resolve the bug 129035, a text form control elements needs to synchronously change
its inner text element's editability by setting or unsetting contenteditable content attribute.
Before this patch, we could not do this because editability of a text form control dependent on
its disabled-ness which was only computed lazily via updateAncestorDisabledState().

This patch makes HTMLFieldSetElement and HTMLFormControlElement update this state synchronously.
To avoid O(k) DOM traversal, where k is the depth of the tree, in insertedInto and removedFrom of
HTMLFormControlElement on most pages, a new document-level flag, m_disabledFieldsetElementsCount,
has been added to indicate whether the document contains any disabled fieldset or not.

Also renamed the misleadingly named disabledAttributeChanged to disabledStateChanged, and added
new function of the same name (disabledAttributeChanged) to be used by HTMLFieldSetElement
for keeping the document-level flag up-to-date upon disabled attribute changes.

Tests: fast/forms/fieldset/fieldset-disabled-2.html

* dom/Document.cpp:
(WebCore::Document::Document): Initializes newly added m_disabledFieldsetElementsCount.
(WebCore::Document::~Document): Assert that we've done house keeping right.
* dom/Document.h:
(WebCore::Document::hasDisabledFieldsetElement): Added.
(WebCore::Document::addDisabledFieldsetElement): Added.
(WebCore::Document::removeDisabledFieldsetElement): Added.

* html/HTMLFieldSetElement.cpp:
(WebCore::HTMLFieldSetElement::~HTMLFieldSetElement): Removes itself from the owner document.

(WebCore::updateFromControlElementsAncestorDisabledStateUnder): Added. Updates startNode and
its descendants' ancestor disabled flag. We don't update controls under another disabled
fieldset element since disabled-ness of those controls aren't affected by startNode.

(WebCore::HTMLFieldSetElement::disabledAttributeChanged): Call addDisabledFieldsetElement and
removeDisabledFieldsetElement to update the owner document's flag.

(WebCore::HTMLFieldSetElement::disabledStateChanged): Renamed from disabledAttributeChanged. 
Enable form control elements under the first legend element and disable or enable other
descendent form controls in accordance with the presence of disabled content attribute.

(WebCore::HTMLFieldSetElement::childrenChanged): Update disabled-ness of form controls under
child legend elements because controls aren't disabled in the first legend element, and adding
or removing child elements may have changed the first legend element.

(WebCore::HTMLFieldSetElement::didMoveToNewDocument): Update the flag on the owner document.
* html/HTMLFieldSetElement.h:

* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::HTMLFormControlElement):
(WebCore::HTMLFormControlElement::computeIsDisabledByFieldsetAncestor): Returns boolean instead of
updating m_ancestorDisabledState internally. Also renamed from updateAncestorDisabledState.

(WebCore::HTMLFormControlElement::setAncestorDisabled): Replaced ancestorDisabledStateWasChanged.
This function updates m_disabledByAncestorFieldset and calls disabledAttributeChanged as needed.

(WebCore::HTMLFormControlElement::disabledAttributeChanged): Added. Calls disabledStateChanged.
(WebCore::HTMLFormControlElement::disabledStateChanged): Renamed from disabledAttributeChanged.

(WebCore::HTMLFormControlElement::insertedInto): Update m_disabledByAncestorFieldset if there is
a possibility (i.e. the document contains any disabled fieldset element) that this form control
is inserted under a disabled fieldset element.

(WebCore::HTMLFormControlElement::removedFrom): If this form control element is not disabled by
a fieldset ancestor, then there is nothing to do. If it is, then check to see if the element is
still disabled now that we've lost some ancestors.

(WebCore::HTMLFormControlElement::isDisabledFormControl): No longer updates m_ancestorDisabledState
lazily since m_disabledByAncestorFieldset is never ambiguous now.

* html/HTMLFormControlElement.h:
(WebCore::HTMLFormControlElement::disabledByAncestorFieldset): Added.

LayoutTests: 

Added more test cases.

* fast/forms/fieldset/fieldset-disabled-2-expected.txt:
* fast/forms/fieldset/fieldset-disabled-2.html:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastformsfieldsetfieldsetdisabled2expectedtxt">trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastformsfieldsetfieldsetdisabled2html">trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLFieldSetElementcpp">trunk/Source/WebCore/html/HTMLFieldSetElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLFieldSetElementh">trunk/Source/WebCore/html/HTMLFieldSetElement.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLFormControlElementcpp">trunk/Source/WebCore/html/HTMLFormControlElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLFormControlElementh">trunk/Source/WebCore/html/HTMLFormControlElement.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/LayoutTests/ChangeLog        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2014-02-19  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        m_ancestorDisabledState should never be unknown
+        https://bugs.webkit.org/show_bug.cgi?id=129084
+
+        Reviewed by Benjamin Poulain.
+
+        Added more test cases.
+
+        * fast/forms/fieldset/fieldset-disabled-2-expected.txt:
+        * fast/forms/fieldset/fieldset-disabled-2.html:
+
</ins><span class="cx"> 2014-02-20  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [iOS] Assert / crash trying to draw empty checked input
</span></span></pre></div>
<a id="trunkLayoutTestsfastformsfieldsetfieldsetdisabled2expectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2-expected.txt (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2-expected.txt        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2-expected.txt        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -15,6 +15,25 @@
</span><span class="cx"> PASS isInputDisabledById(&quot;inputInsideSecondLegend&quot;) is false
</span><span class="cx"> PASS isInputDisabledById(&quot;inputInsideNestedFirstLegend&quot;) is false
</span><span class="cx"> PASS isInputDisabledById(&quot;inputInsideFirstLegendWithDisabledOuterFieldset&quot;) is false
</span><ins>+
+contentDocument = createIframe();
+fieldset = contentDocument.createElement(&quot;fieldset&quot;); contentDocument.body.appendChild(fieldset);
+PASS input = contentDocument.createElement(&quot;input&quot;); fieldset.appendChild(input); isInputDisabled(input) is false
+PASS fieldset.disabled = true; isInputDisabled(input) is true
+PASS contentDocument.body.appendChild(input); isInputDisabled(input) is false
+PASS document.body.appendChild(input); isInputDisabled(input) is false
+PASS fieldset.appendChild(input); isInputDisabled(input) is true
+fieldset2 = document.createElement(&quot;fieldset&quot;); fieldset2.disabled = true
+input2 = document.createElement(&quot;input&quot;); fieldset2.appendChild(input2)
+PASS contentDocument.body.appendChild(fieldset2); isInputDisabled(input2) is true
+PASS document.body.appendChild(fieldset); isInputDisabled(input) is true
+
+setDisabledOnAllFieldsets(true)
+PASS isInputDisabledById(&quot;inputOutsideLegend&quot;) is true
+PASS isInputDisabledById(&quot;inputInsideFirstLegend&quot;) is false
+PASS isInputDisabledById(&quot;inputInsideSecondLegend&quot;) is true
+PASS isInputDisabledById(&quot;inputInsideNestedFirstLegend&quot;) is false
+PASS isInputDisabledById(&quot;inputInsideFirstLegendWithDisabledOuterFieldset&quot;) is true
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestsfastformsfieldsetfieldsetdisabled2html"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2.html (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2.html        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/LayoutTests/fast/forms/fieldset/fieldset-disabled-2.html        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -18,11 +18,14 @@
</span><span class="cx"> 
</span><span class="cx"> description(&quot;Additional tests for fieldset element disabling descendent input elements&quot;);
</span><span class="cx"> 
</span><del>-function isInputDisabledById(id) {
-    var input = document.getElementById(id);
</del><ins>+function isInputDisabled(input) {
</ins><span class="cx">     return getComputedStyle(input).color == 'rgb(102, 102, 102)';
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+function isInputDisabledById(id) {
+    return isInputDisabled(document.getElementById(id));
+}
+
</ins><span class="cx"> shouldBeTrue('isInputDisabledById(&quot;inputOutsideLegend&quot;)');
</span><span class="cx"> shouldBeFalse('isInputDisabledById(&quot;inputInsideFirstLegend&quot;)');
</span><span class="cx"> shouldBeTrue('isInputDisabledById(&quot;inputInsideSecondLegend&quot;)');
</span><span class="lines">@@ -30,7 +33,7 @@
</span><span class="cx"> shouldBeTrue('isInputDisabledById(&quot;inputInsideFirstLegendWithDisabledOuterFieldset&quot;)');
</span><span class="cx"> 
</span><span class="cx"> function setDisabledOnAllFieldsets(value) {
</span><del>-    var fieldsets = document.querySelectorAll('fieldset');
</del><ins>+    var fieldsets = document.querySelectorAll('#container &gt; fieldset, body &gt; fieldset');
</ins><span class="cx">     for (var i = 0; i &lt; fieldsets.length; i++)
</span><span class="cx">         fieldsets[i].disabled = value;
</span><span class="cx"> }
</span><span class="lines">@@ -43,6 +46,40 @@
</span><span class="cx"> shouldBeFalse('isInputDisabledById(&quot;inputInsideNestedFirstLegend&quot;)');
</span><span class="cx"> shouldBeFalse('isInputDisabledById(&quot;inputInsideFirstLegendWithDisabledOuterFieldset&quot;)');
</span><span class="cx"> 
</span><ins>+function createIframe() {
+    var iframe = document.createElement(&quot;iframe&quot;);
+    document.getElementById('container').appendChild(iframe);
+    iframe.contentDocument.head.appendChild(document.querySelector('body style').cloneNode(true));
+    return iframe.contentDocument;
+}
+
+function insertNewFieldsetIntoBody(contentDocument) {
+    fieldset = contentDocument.createElement(&quot;fieldset&quot;);
+    contentDocument.body.appendChild(fieldset);
+    return fieldset;
+}
+
+debug('');
+evalAndLog('contentDocument = createIframe();');
+evalAndLog('fieldset = contentDocument.createElement(&quot;fieldset&quot;); contentDocument.body.appendChild(fieldset);');
+shouldBeFalse('input = contentDocument.createElement(&quot;input&quot;); fieldset.appendChild(input); isInputDisabled(input)');
+shouldBeTrue('fieldset.disabled = true; isInputDisabled(input)');
+shouldBeFalse('contentDocument.body.appendChild(input); isInputDisabled(input)');
+shouldBeFalse('document.body.appendChild(input); isInputDisabled(input)');
+shouldBeTrue('fieldset.appendChild(input); isInputDisabled(input)');
+evalAndLog('fieldset2 = document.createElement(&quot;fieldset&quot;); fieldset2.disabled = true');
+evalAndLog('input2 = document.createElement(&quot;input&quot;); fieldset2.appendChild(input2)');
+shouldBeTrue('contentDocument.body.appendChild(fieldset2); isInputDisabled(input2)');
+shouldBeTrue('document.body.appendChild(fieldset); isInputDisabled(input)');
+
+debug('');
+evalAndLog('setDisabledOnAllFieldsets(true)');
+shouldBeTrue('isInputDisabledById(&quot;inputOutsideLegend&quot;)');
+shouldBeFalse('isInputDisabledById(&quot;inputInsideFirstLegend&quot;)');
+shouldBeTrue('isInputDisabledById(&quot;inputInsideSecondLegend&quot;)');
+shouldBeFalse('isInputDisabledById(&quot;inputInsideNestedFirstLegend&quot;)');
+shouldBeTrue('isInputDisabledById(&quot;inputInsideFirstLegendWithDisabledOuterFieldset&quot;)');
+
</ins><span class="cx"> document.getElementById('container').style.display = 'none';
</span><span class="cx"> 
</span><span class="cx"> &lt;/script&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/ChangeLog        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -1,3 +1,80 @@
</span><ins>+2014-02-20  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        m_ancestorDisabledState should never be unknown
+        https://bugs.webkit.org/show_bug.cgi?id=129084
+
+        Reviewed by Benjamin Poulain.
+
+        In order to resolve the bug 129035, a text form control elements needs to synchronously change
+        its inner text element's editability by setting or unsetting contenteditable content attribute.
+        Before this patch, we could not do this because editability of a text form control dependent on
+        its disabled-ness which was only computed lazily via updateAncestorDisabledState().
+
+        This patch makes HTMLFieldSetElement and HTMLFormControlElement update this state synchronously.
+        To avoid O(k) DOM traversal, where k is the depth of the tree, in insertedInto and removedFrom of
+        HTMLFormControlElement on most pages, a new document-level flag, m_disabledFieldsetElementsCount,
+        has been added to indicate whether the document contains any disabled fieldset or not.
+
+        Also renamed the misleadingly named disabledAttributeChanged to disabledStateChanged, and added
+        new function of the same name (disabledAttributeChanged) to be used by HTMLFieldSetElement
+        for keeping the document-level flag up-to-date upon disabled attribute changes.
+
+        Tests: fast/forms/fieldset/fieldset-disabled-2.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document): Initializes newly added m_disabledFieldsetElementsCount.
+        (WebCore::Document::~Document): Assert that we've done house keeping right.
+        * dom/Document.h:
+        (WebCore::Document::hasDisabledFieldsetElement): Added.
+        (WebCore::Document::addDisabledFieldsetElement): Added.
+        (WebCore::Document::removeDisabledFieldsetElement): Added.
+
+        * html/HTMLFieldSetElement.cpp:
+        (WebCore::HTMLFieldSetElement::~HTMLFieldSetElement): Removes itself from the owner document.
+
+        (WebCore::updateFromControlElementsAncestorDisabledStateUnder): Added. Updates startNode and
+        its descendants' ancestor disabled flag. We don't update controls under another disabled
+        fieldset element since disabled-ness of those controls aren't affected by startNode.
+
+        (WebCore::HTMLFieldSetElement::disabledAttributeChanged): Call addDisabledFieldsetElement and
+        removeDisabledFieldsetElement to update the owner document's flag.
+
+        (WebCore::HTMLFieldSetElement::disabledStateChanged): Renamed from disabledAttributeChanged. 
+        Enable form control elements under the first legend element and disable or enable other
+        descendent form controls in accordance with the presence of disabled content attribute.
+
+        (WebCore::HTMLFieldSetElement::childrenChanged): Update disabled-ness of form controls under
+        child legend elements because controls aren't disabled in the first legend element, and adding
+        or removing child elements may have changed the first legend element.
+
+        (WebCore::HTMLFieldSetElement::didMoveToNewDocument): Update the flag on the owner document.
+        * html/HTMLFieldSetElement.h:
+
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::HTMLFormControlElement):
+        (WebCore::HTMLFormControlElement::computeIsDisabledByFieldsetAncestor): Returns boolean instead of
+        updating m_ancestorDisabledState internally. Also renamed from updateAncestorDisabledState.
+
+        (WebCore::HTMLFormControlElement::setAncestorDisabled): Replaced ancestorDisabledStateWasChanged.
+        This function updates m_disabledByAncestorFieldset and calls disabledAttributeChanged as needed.
+
+        (WebCore::HTMLFormControlElement::disabledAttributeChanged): Added. Calls disabledStateChanged.
+        (WebCore::HTMLFormControlElement::disabledStateChanged): Renamed from disabledAttributeChanged.
+
+        (WebCore::HTMLFormControlElement::insertedInto): Update m_disabledByAncestorFieldset if there is
+        a possibility (i.e. the document contains any disabled fieldset element) that this form control
+        is inserted under a disabled fieldset element.
+
+        (WebCore::HTMLFormControlElement::removedFrom): If this form control element is not disabled by
+        a fieldset ancestor, then there is nothing to do. If it is, then check to see if the element is
+        still disabled now that we've lost some ancestors.
+
+        (WebCore::HTMLFormControlElement::isDisabledFormControl): No longer updates m_ancestorDisabledState
+        lazily since m_disabledByAncestorFieldset is never ambiguous now.
+
+        * html/HTMLFormControlElement.h:
+        (WebCore::HTMLFormControlElement::disabledByAncestorFieldset): Added.
+
</ins><span class="cx"> 2014-02-20  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Remove redundant deviceScaleFactor() functions and make callers use Document::deviceScaleFactor() when accessible.
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/dom/Document.cpp        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -502,6 +502,7 @@
</span><span class="cx">     , m_inputCursor(EmptyInputCursor::create())
</span><span class="cx"> #endif
</span><span class="cx">     , m_didAssociateFormControlsTimer(this, &amp;Document::didAssociateFormControlsTimerFired)
</span><ins>+    , m_disabledFieldsetElementsCount(0)
</ins><span class="cx">     , m_hasInjectedPlugInsScript(false)
</span><span class="cx">     , m_renderTreeBeingDestroyed(false)
</span><span class="cx"> {
</span><span class="lines">@@ -572,6 +573,7 @@
</span><span class="cx">     ASSERT(!m_inPageCache);
</span><span class="cx">     ASSERT(m_ranges.isEmpty());
</span><span class="cx">     ASSERT(!m_parentTreeScope);
</span><ins>+    ASSERT(!m_disabledFieldsetElementsCount);
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(DEVICE_ORIENTATION) &amp;&amp; PLATFORM(IOS)
</span><span class="cx">     m_deviceMotionClient-&gt;deviceMotionControllerDestroyed();
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/dom/Document.h        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -1239,6 +1239,9 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     void didAssociateFormControl(Element*);
</span><ins>+    bool hasDisabledFieldsetElement() const { return m_disabledFieldsetElementsCount; }
+    void addDisabledFieldsetElement() { m_disabledFieldsetElementsCount++; }
+    void removeDisabledFieldsetElement() { ASSERT(m_disabledFieldsetElementsCount); m_disabledFieldsetElementsCount--; }
</ins><span class="cx"> 
</span><span class="cx">     virtual void addConsoleMessage(MessageSource, MessageLevel, const String&amp; message, unsigned long requestIdentifier = 0) override;
</span><span class="cx"> 
</span><span class="lines">@@ -1676,6 +1679,7 @@
</span><span class="cx"> 
</span><span class="cx">     Timer&lt;Document&gt; m_didAssociateFormControlsTimer;
</span><span class="cx">     HashSet&lt;RefPtr&lt;Element&gt;&gt; m_associatedFormControls;
</span><ins>+    unsigned m_disabledFieldsetElementsCount;
</ins><span class="cx"> 
</span><span class="cx">     bool m_hasInjectedPlugInsScript;
</span><span class="cx">     bool m_renderTreeBeingDestroyed;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLFieldSetElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLFieldSetElement.cpp (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLFieldSetElement.cpp        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/html/HTMLFieldSetElement.cpp        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -44,32 +44,90 @@
</span><span class="cx">     ASSERT(hasTagName(fieldsetTag));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+HTMLFieldSetElement::~HTMLFieldSetElement()
+{
+    if (hasAttribute(disabledAttr))
+        document().removeDisabledFieldsetElement();
+}
+
</ins><span class="cx"> PassRefPtr&lt;HTMLFieldSetElement&gt; HTMLFieldSetElement::create(const QualifiedName&amp; tagName, Document&amp; document, HTMLFormElement* form)
</span><span class="cx"> {
</span><span class="cx">     return adoptRef(new HTMLFieldSetElement(tagName, document, form));
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLFieldSetElement::invalidateDisabledStateUnder(Element* base)
</del><ins>+static void updateFromControlElementsAncestorDisabledStateUnder(HTMLElement&amp; startNode, bool isDisabled)
</ins><span class="cx"> {
</span><del>-    for (auto&amp; control : descendantsOfType&lt;HTMLFormControlElement&gt;(*base))
-        control.ancestorDisabledStateWasChanged();
</del><ins>+    HTMLFormControlElement* control;
+    if (isHTMLFormControlElement(startNode))
+        control = &amp;toHTMLFormControlElement(startNode);
+    else
+        control = Traversal&lt;HTMLFormControlElement&gt;::firstWithin(&amp;startNode);
+    while (control) {
+        control-&gt;setAncestorDisabled(isDisabled);
+        // Don't call setAncestorDisabled(false) on form contorls inside disabled fieldsets.
+        if (isHTMLFieldSetElement(control) &amp;&amp; control-&gt;hasAttribute(disabledAttr))
+            control = Traversal&lt;HTMLFormControlElement&gt;::nextSkippingChildren(control, &amp;startNode);
+        else
+            control = Traversal&lt;HTMLFormControlElement&gt;::next(control, &amp;startNode);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLFieldSetElement::disabledAttributeChanged()
</span><span class="cx"> {
</span><del>-    // This element must be updated before the style of nodes in its subtree gets recalculated.
</del><ins>+    if (hasAttribute(disabledAttr))
+        document().addDisabledFieldsetElement();
+    else
+        document().removeDisabledFieldsetElement();
+
</ins><span class="cx">     HTMLFormControlElement::disabledAttributeChanged();
</span><del>-    invalidateDisabledStateUnder(this);
</del><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLFieldSetElement::disabledStateChanged()
+{
+    // This element must be updated before the style of nodes in its subtree gets recalculated.
+    HTMLFormControlElement::disabledStateChanged();
+
+    if (disabledByAncestorFieldset())
+        return;
+
+    bool thisFieldsetIsDisabled = hasAttribute(disabledAttr);
+    bool hasSeenFirstLegendElement = false;
+    for (auto&amp; control : childrenOfType&lt;HTMLElement&gt;(*this)) {
+        if (!hasSeenFirstLegendElement &amp;&amp; isHTMLLegendElement(control)) {
+            hasSeenFirstLegendElement = true;
+            updateFromControlElementsAncestorDisabledStateUnder(control, false /* isDisabled */);
+            continue;
+        }
+        updateFromControlElementsAncestorDisabledStateUnder(control, thisFieldsetIsDisabled);
+    }
+}
+
</ins><span class="cx"> void HTMLFieldSetElement::childrenChanged(const ChildChange&amp; change)
</span><span class="cx"> {
</span><span class="cx">     HTMLFormControlElement::childrenChanged(change);
</span><ins>+    if (!hasAttribute(disabledAttr))
+        return;
</ins><span class="cx"> 
</span><del>-    for (auto&amp; legend : childrenOfType&lt;HTMLLegendElement&gt;(*this))
-        invalidateDisabledStateUnder(&amp;legend);
</del><ins>+    HTMLLegendElement* legend = Traversal&lt;HTMLLegendElement&gt;::firstChild(this);
+    if (!legend)
+        return;
+
+    // We only care about the first legend element (in which form contorls are not disabled by this element) changing here.
+    updateFromControlElementsAncestorDisabledStateUnder(*legend, false /* isDisabled */);
+    while ((legend = Traversal&lt;HTMLLegendElement&gt;::nextSibling(legend)))
+        updateFromControlElementsAncestorDisabledStateUnder(*legend, true);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLFieldSetElement::didMoveToNewDocument(Document* oldDocument)
+{
+    HTMLFormControlElement::didMoveToNewDocument(oldDocument);
+    if (hasAttribute(disabledAttr)) {
+        if (oldDocument)
+            oldDocument-&gt;removeDisabledFieldsetElement();
+        document().addDisabledFieldsetElement();
+    }
+}
+
</ins><span class="cx"> bool HTMLFieldSetElement::supportsFocus() const
</span><span class="cx"> {
</span><span class="cx">     return HTMLElement::supportsFocus();
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLFieldSetElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLFieldSetElement.h (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLFieldSetElement.h        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/html/HTMLFieldSetElement.h        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -42,19 +42,21 @@
</span><span class="cx">     unsigned length() const;
</span><span class="cx"> 
</span><span class="cx"> protected:
</span><del>-    virtual void disabledAttributeChanged() override;
</del><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     HTMLFieldSetElement(const QualifiedName&amp;, Document&amp;, HTMLFormElement*);
</span><ins>+    ~HTMLFieldSetElement();
</ins><span class="cx"> 
</span><span class="cx">     virtual bool isEnumeratable() const override { return true; }
</span><span class="cx">     virtual bool supportsFocus() const override;
</span><span class="cx">     virtual RenderPtr&lt;RenderElement&gt; createElementRenderer(PassRef&lt;RenderStyle&gt;) override;
</span><span class="cx">     virtual const AtomicString&amp; formControlType() const override;
</span><span class="cx">     virtual bool recalcWillValidate() const override { return false; }
</span><ins>+    virtual void disabledAttributeChanged() override;
+    virtual void disabledStateChanged() override;
</ins><span class="cx">     virtual void childrenChanged(const ChildChange&amp;) override;
</span><ins>+    virtual void didMoveToNewDocument(Document* oldDocument) override;
</ins><span class="cx"> 
</span><del>-    static void invalidateDisabledStateUnder(Element*);
</del><span class="cx">     void refreshElementsIfNeeded() const;
</span><span class="cx"> 
</span><span class="cx">     mutable Vector&lt;FormAssociatedElement*&gt; m_associatedElements;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLFormControlElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -52,7 +52,7 @@
</span><span class="cx">     , m_isReadOnly(false)
</span><span class="cx">     , m_isRequired(false)
</span><span class="cx">     , m_valueMatchesRenderer(false)
</span><del>-    , m_ancestorDisabledState(AncestorDisabledStateUnknown)
</del><ins>+    , m_disabledByAncestorFieldset(false)
</ins><span class="cx">     , m_dataListAncestorState(Unknown)
</span><span class="cx">     , m_willValidateInitialized(false)
</span><span class="cx">     , m_willValidate(true)
</span><span class="lines">@@ -99,25 +99,27 @@
</span><span class="cx">     return fastHasAttribute(formnovalidateAttr);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLFormControlElement::updateAncestorDisabledState() const
</del><ins>+bool HTMLFormControlElement::computeIsDisabledByFieldsetAncestor() const
</ins><span class="cx"> {
</span><span class="cx">     Element* previousAncestor = nullptr;
</span><span class="cx">     for (Element* ancestor = parentElement(); ancestor; ancestor = ancestor-&gt;parentElement()) {
</span><span class="cx">         if (isHTMLFieldSetElement(ancestor) &amp;&amp; ancestor-&gt;hasAttribute(disabledAttr)) {
</span><span class="cx">             HTMLFieldSetElement&amp; fieldSetAncestor = toHTMLFieldSetElement(*ancestor);
</span><span class="cx">             bool isInFirstLegend = previousAncestor &amp;&amp; isHTMLLegendElement(previousAncestor) &amp;&amp; previousAncestor == fieldSetAncestor.legend();
</span><del>-            m_ancestorDisabledState = isInFirstLegend ? AncestorDisabledStateEnabled : AncestorDisabledStateDisabled;
-            return;
</del><ins>+            return !isInFirstLegend;
</ins><span class="cx">         }
</span><span class="cx">         previousAncestor = ancestor;
</span><span class="cx">     }
</span><del>-    m_ancestorDisabledState = AncestorDisabledStateEnabled;
</del><ins>+    return false;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLFormControlElement::ancestorDisabledStateWasChanged()
</del><ins>+void HTMLFormControlElement::setAncestorDisabled(bool isDisabled)
</ins><span class="cx"> {
</span><del>-    m_ancestorDisabledState = AncestorDisabledStateUnknown;
-    disabledAttributeChanged();
</del><ins>+    ASSERT(computeIsDisabledByFieldsetAncestor() == isDisabled);
+    bool oldValue = m_disabledByAncestorFieldset;
+    m_disabledByAncestorFieldset = isDisabled;
+    if (oldValue != m_disabledByAncestorFieldset)
+        disabledStateChanged();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLFormControlElement::parseAttribute(const QualifiedName&amp; name, const AtomicString&amp; value)
</span><span class="lines">@@ -154,6 +156,11 @@
</span><span class="cx"> 
</span><span class="cx"> void HTMLFormControlElement::disabledAttributeChanged()
</span><span class="cx"> {
</span><ins>+    disabledStateChanged();
+}
+
+void HTMLFormControlElement::disabledStateChanged()
+{
</ins><span class="cx">     setNeedsWillValidateCheck();
</span><span class="cx">     didAffectSelector(AffectedSelectorDisabled | AffectedSelectorEnabled);
</span><span class="cx">     if (renderer() &amp;&amp; renderer()-&gt;style().hasAppearance())
</span><span class="lines">@@ -231,7 +238,8 @@
</span><span class="cx"> 
</span><span class="cx"> Node::InsertionNotificationRequest HTMLFormControlElement::insertedInto(ContainerNode&amp; insertionPoint)
</span><span class="cx"> {
</span><del>-    m_ancestorDisabledState = AncestorDisabledStateUnknown;
</del><ins>+    if (document().hasDisabledFieldsetElement())
+        setAncestorDisabled(computeIsDisabledByFieldsetAncestor());
</ins><span class="cx">     m_dataListAncestorState = Unknown;
</span><span class="cx">     setNeedsWillValidateCheck();
</span><span class="cx">     HTMLElement::insertedInto(insertionPoint);
</span><span class="lines">@@ -242,7 +250,8 @@
</span><span class="cx"> void HTMLFormControlElement::removedFrom(ContainerNode&amp; insertionPoint)
</span><span class="cx"> {
</span><span class="cx">     m_validationMessage = nullptr;
</span><del>-    m_ancestorDisabledState = AncestorDisabledStateUnknown;
</del><ins>+    if (m_disabledByAncestorFieldset)
+        setAncestorDisabled(computeIsDisabledByFieldsetAncestor());
</ins><span class="cx">     m_dataListAncestorState = Unknown;
</span><span class="cx">     HTMLElement::removedFrom(insertionPoint);
</span><span class="cx">     FormAssociatedElement::removedFrom(insertionPoint);
</span><span class="lines">@@ -272,14 +281,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool HTMLFormControlElement::isDisabledFormControl() const
</span><span class="cx"> {
</span><del>-    if (m_disabled)
-        return true;
-
-    if (m_ancestorDisabledState == AncestorDisabledStateUnknown)
-        updateAncestorDisabledState();
-    if (m_ancestorDisabledState == AncestorDisabledStateDisabled)
-        return true;
-    return HTMLElement::isDisabledFormControl();
</del><ins>+    return m_disabled || m_disabledByAncestorFieldset;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool HTMLFormControlElement::isRequired() const
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLFormControlElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLFormControlElement.h (164474 => 164475)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLFormControlElement.h        2014-02-21 06:32:11 UTC (rev 164474)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.h        2014-02-21 06:40:05 UTC (rev 164475)
</span><span class="lines">@@ -54,7 +54,7 @@
</span><span class="cx">     void setFormMethod(const String&amp;);
</span><span class="cx">     bool formNoValidate() const;
</span><span class="cx"> 
</span><del>-    void ancestorDisabledStateWasChanged();
</del><ins>+    void setAncestorDisabled(bool isDisabled);
</ins><span class="cx"> 
</span><span class="cx">     virtual void reset() { }
</span><span class="cx"> 
</span><span class="lines">@@ -120,9 +120,12 @@
</span><span class="cx"> protected:
</span><span class="cx">     HTMLFormControlElement(const QualifiedName&amp; tagName, Document&amp;, HTMLFormElement*);
</span><span class="cx"> 
</span><ins>+    bool disabledByAncestorFieldset() const { return m_disabledByAncestorFieldset; }
+
</ins><span class="cx">     virtual void parseAttribute(const QualifiedName&amp;, const AtomicString&amp;) override;
</span><span class="cx">     virtual void requiredAttributeChanged();
</span><span class="cx">     virtual void disabledAttributeChanged();
</span><ins>+    virtual void disabledStateChanged();
</ins><span class="cx">     virtual void didAttachRenderers() override;
</span><span class="cx">     virtual InsertionNotificationRequest insertedInto(ContainerNode&amp;) override;
</span><span class="cx">     virtual void removedFrom(ContainerNode&amp;) override;
</span><span class="lines">@@ -154,8 +157,9 @@
</span><span class="cx">     virtual HTMLFormElement* virtualForm() const override;
</span><span class="cx">     virtual bool isDefaultButtonForForm() const override;
</span><span class="cx">     virtual bool isValidFormControlElement() const override;
</span><del>-    void updateAncestorDisabledState() const;
</del><span class="cx"> 
</span><ins>+    bool computeIsDisabledByFieldsetAncestor() const;
+
</ins><span class="cx">     virtual HTMLElement&amp; asHTMLElement() override final { return *this; }
</span><span class="cx">     virtual const HTMLFormControlElement&amp; asHTMLElement() const override final { return *this; }
</span><span class="cx">     virtual HTMLFormControlElement* asFormNamedItem() override final { return this; }
</span><span class="lines">@@ -165,9 +169,8 @@
</span><span class="cx">     bool m_isReadOnly : 1;
</span><span class="cx">     bool m_isRequired : 1;
</span><span class="cx">     bool m_valueMatchesRenderer : 1;
</span><ins>+    bool m_disabledByAncestorFieldset : 1;
</ins><span class="cx"> 
</span><del>-    enum AncestorDisabledState { AncestorDisabledStateUnknown, AncestorDisabledStateEnabled, AncestorDisabledStateDisabled };
-    mutable AncestorDisabledState m_ancestorDisabledState;
</del><span class="cx">     enum DataListAncestorState { Unknown, InsideDataList, NotInsideDataList };
</span><span class="cx">     mutable enum DataListAncestorState m_dataListAncestorState;
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>