:indeterminate pseudo-class should match radios whose group has no checked radio
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 18 Jun 2016 05:53:28 +0000 (05:53 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 18 Jun 2016 05:53:28 +0000 (05:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156270

Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/html/semantics/selectors/pseudo-classes/indeterminate-expected.txt:
One more pass on official tests :)

Source/WebCore:

The pseudo-class ":indeterminate" is supposed to match radio buttons
for which the entire group has no checked button.
Spec: https://html.spec.whatwg.org/#pseudo-classes:selector-indeterminate

The change is straightforward with one non-obvious choice:
I added matchesIndeterminatePseudoClass() in addition to shouldAppearIndeterminate().

The reason is shouldAppearIndeterminate() is used for styling and AX of elements
with an indeterminate states (check boxes and progress element). There is no such
UI for radio boxes.
I could have extended shouldAppearIndeterminate() to radio box
then filter out this case in RenderTheme. The problem is doing that would also requires
changes to the repaint logic to match :indeterminate. It seemed overkill to me to
change repaint() for a case that is never used in practice.

Tests: fast/css/pseudo-indeterminate-radio-buttons-basics.html
       fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html
       fast/selectors/detached-radio-button-checked-and-indeterminate-states.html
       fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html

* css/SelectorCheckerTestFunctions.h:
(WebCore::shouldAppearIndeterminate):
* dom/Element.cpp:
(WebCore::Element::matchesIndeterminatePseudoClass):
* dom/Element.h:
* dom/RadioButtonGroups.cpp:
(WebCore::RadioButtonGroup::setCheckedButton):
(WebCore::RadioButtonGroup::updateCheckedState):
(WebCore::RadioButtonGroup::remove):
(WebCore::RadioButtonGroup::setNeedsStyleRecalcForAllButtons):
(WebCore::RadioButtonGroups::hasCheckedButton):
* dom/RadioButtonGroups.h:
* html/CheckboxInputType.cpp:
(WebCore::CheckboxInputType::matchesIndeterminatePseudoClass):
(WebCore::CheckboxInputType::shouldAppearIndeterminate):
(WebCore::CheckboxInputType::supportsIndeterminateAppearance): Deleted.
* html/CheckboxInputType.h:
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::setChecked):
(WebCore::HTMLInputElement::matchesIndeterminatePseudoClass):
(WebCore::HTMLInputElement::shouldAppearIndeterminate):
(WebCore::HTMLInputElement::radioButtonGroups):
* html/HTMLInputElement.h:
* html/InputType.cpp:
(WebCore::InputType::matchesIndeterminatePseudoClass):
(WebCore::InputType::shouldAppearIndeterminate):
(WebCore::InputType::supportsIndeterminateAppearance): Deleted.
* html/InputType.h:
* html/RadioInputType.cpp:
(WebCore::RadioInputType::matchesIndeterminatePseudoClass):
(WebCore::RadioInputType::willDispatchClick): Deleted.
(WebCore::RadioInputType::didDispatchClick): Deleted.
(WebCore::RadioInputType::supportsIndeterminateAppearance): Deleted.
The iOS specific code is just plain wrong.
It was changing the indeterminate state of the input element.
The spec clearly says that state is only used by checkbox:
https://html.spec.whatwg.org/#dom-input-indeterminate

Moreover, the style update would not change the indeterminate state
of other buttons in the Button Group, which is just bizarre.
RenderThemeIOS does not make use of any of this with the current style.

* html/RadioInputType.h:
* style/StyleSharingResolver.cpp:
(WebCore::Style::SharingResolver::canShareStyleWithElement):
(WebCore::Style::canShareStyleWithControl): Deleted.
(WebCore::Style::SharingResolver::sharingCandidateHasIdenticalStyleAffectingAttributes): Deleted.
Style sharing is unified behind the selector matching which is neat.

LayoutTests:

There are two important aspect to cover for this change:
1) The style is updated correctly when a Button Group composition change.
2) When the checkness changes for a Button Group, all its elements
   are invalidated to match :indeterminate.

* fast/forms/radio/indeterminate-radio.html:
This test was verifying that the property "indeterminate" of the input element
is not reflected to the style through :indeterminate.
I updated the test to still verify that except that we now match :indeterminate
before changing the property.

* fast/css/pseudo-indeterminate-radio-buttons-basics-expected.html: Added.
* fast/css/pseudo-indeterminate-radio-buttons-basics.html: Added.

* fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation-expected.txt: Added.
* fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html: Added.
Verify that we don't invalidate everything when the checked button changes.
We only need to invalidate everything if the checked state of the whole group changes.

* fast/selectors/detached-radio-button-checked-and-indeterminate-states-expected.txt: Added.
* fast/selectors/detached-radio-button-checked-and-indeterminate-states.html: Added.
* fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update-expected.txt: Added.
* fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@202197 268f45cc-cd09-0410-ab3c-d52691b4dbfc

30 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/pseudo-indeterminate-radio-buttons-basics-expected.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-indeterminate-radio-buttons-basics.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation-expected.txt [new file with mode: 0644]
LayoutTests/fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html [new file with mode: 0644]
LayoutTests/fast/forms/radio/indeterminate-radio.html
LayoutTests/fast/selectors/detached-radio-button-checked-and-indeterminate-states-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/detached-radio-button-checked-and-indeterminate-states.html [new file with mode: 0644]
LayoutTests/fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/indeterminate-expected.txt
LayoutTests/platform/ios-simulator/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-expected.txt [deleted file]
Source/WebCore/ChangeLog
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/css/SelectorCheckerTestFunctions.h
Source/WebCore/cssjit/SelectorCompiler.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/RadioButtonGroups.cpp
Source/WebCore/dom/RadioButtonGroups.h
Source/WebCore/html/CheckboxInputType.cpp
Source/WebCore/html/CheckboxInputType.h
Source/WebCore/html/HTMLInputElement.cpp
Source/WebCore/html/HTMLInputElement.h
Source/WebCore/html/InputType.cpp
Source/WebCore/html/InputType.h
Source/WebCore/html/RadioInputType.cpp
Source/WebCore/html/RadioInputType.h
Source/WebCore/style/StyleSharingResolver.cpp

index 6d1f955..491f0e6 100644 (file)
@@ -1,3 +1,34 @@
+2016-06-17  Benjamin Poulain  <benjamin@webkit.org>
+
+        :indeterminate pseudo-class should match radios whose group has no checked radio
+        https://bugs.webkit.org/show_bug.cgi?id=156270
+
+        Reviewed by Simon Fraser.
+
+        There are two important aspect to cover for this change:
+        1) The style is updated correctly when a Button Group composition change.
+        2) When the checkness changes for a Button Group, all its elements
+           are invalidated to match :indeterminate.
+
+        * fast/forms/radio/indeterminate-radio.html:
+        This test was verifying that the property "indeterminate" of the input element
+        is not reflected to the style through :indeterminate.
+        I updated the test to still verify that except that we now match :indeterminate
+        before changing the property.
+
+        * fast/css/pseudo-indeterminate-radio-buttons-basics-expected.html: Added.
+        * fast/css/pseudo-indeterminate-radio-buttons-basics.html: Added.
+
+        * fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation-expected.txt: Added.
+        * fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html: Added.
+        Verify that we don't invalidate everything when the checked button changes.
+        We only need to invalidate everything if the checked state of the whole group changes.
+
+        * fast/selectors/detached-radio-button-checked-and-indeterminate-states-expected.txt: Added.
+        * fast/selectors/detached-radio-button-checked-and-indeterminate-states.html: Added.
+        * fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update-expected.txt: Added.
+        * fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html: Added.
+
 2016-06-17  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r202152.
diff --git a/LayoutTests/fast/css/pseudo-indeterminate-radio-buttons-basics-expected.html b/LayoutTests/fast/css/pseudo-indeterminate-radio-buttons-basics-expected.html
new file mode 100644 (file)
index 0000000..a225f76
--- /dev/null
@@ -0,0 +1,206 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+    input {
+        background-color: blue;
+        width: 15px;
+        height: 15px;
+        -webkit-appearance: none;
+    }
+    .checked {
+        border: 2px solid red;
+    }
+    .indeterminate {
+        background-color: green;
+    }
+    .disabled {
+        border-left: 3px solid orange;
+    }
+</style>
+</head>
+<body>
+    <div>
+        <input type="radio" class="indeterminate">
+        <input type="radio" class="indeterminate">
+        <input type="radio" name="group1" class="indeterminate">
+        <input type="radio" name="group1" class="indeterminate">
+        <input type="radio" name="group2" class="indeterminate">
+        <span><input type="radio" name="group2" class="indeterminate"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked>
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked></span>
+        <input type="radio" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group6" disabled class="indeterminate disabled">
+        <span><input type="radio" name="group6" disabled class="indeterminate disabled"></span>
+        <input type="radio" name="group7" disabled class="disabled">
+        <input type="radio" name="group7" checked disabled class="disabled">
+        <input type="radio" name="group8" disabled class="disabled">
+        <span><input type="radio" name="group8" checked disabled class="disabled"></span>
+        <input type="radio" readonly class="indeterminate">
+        <input type="radio" name="group9" readonly class="indeterminate">
+        <input type="radio" name="group9" readonly class="indeterminate">
+        <input type="radio" name="group10" readonly class="indeterminate">
+        <span><input type="radio" name="group10" readonly class="indeterminate"></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly>
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly></span>
+    </div>
+    <form>
+        <input type="radio" class="indeterminate">
+        <input type="radio" class="indeterminate">
+        <input type="radio" name="group1" class="indeterminate">
+        <input type="radio" name="group1" class="indeterminate">
+        <input type="radio" name="group2" class="indeterminate">
+        <span><input type="radio" name="group2" class="indeterminate"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked class="checked">
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked class="checked"></span>
+        <input type="radio" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group6" disabled class="indeterminate disabled">
+        <span><input type="radio" name="group6" disabled class="indeterminate disabled"></span>
+        <input type="radio" name="group7" disabled class="disabled">
+        <input type="radio" name="group7" checked disabled class="checked disabled">
+        <input type="radio" name="group8" disabled class="disabled">
+        <span><input type="radio" name="group8" checked disabled class="checked disabled"></span>
+        <input type="radio" readonly class="indeterminate">
+        <input type="radio" name="group9" readonly class="indeterminate">
+        <input type="radio" name="group9" readonly class="indeterminate">
+        <input type="radio" name="group10" readonly class="indeterminate">
+        <span><input type="radio" name="group10" readonly class="indeterminate"></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly class="checked">
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly class="checked"></span>
+    </form>
+    <form disabled>
+        <input type="radio" class="indeterminate">
+        <input type="radio" class="indeterminate">
+        <input type="radio" name="group1" class="indeterminate">
+        <input type="radio" name="group1" class="indeterminate">
+        <input type="radio" name="group2" class="indeterminate">
+        <span><input type="radio" name="group2" class="indeterminate"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked class="checked">
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked class="checked"></span>
+        <input type="radio" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group6" disabled class="indeterminate disabled">
+        <span><input type="radio" name="group6" disabled class="indeterminate disabled"></span>
+        <input type="radio" name="group7" disabled class="disabled">
+        <input type="radio" name="group7" checked disabled class="checked disabled">
+        <input type="radio" name="group8" disabled class="disabled">
+        <span><input type="radio" name="group8" checked disabled class="checked disabled"></span>
+        <input type="radio" readonly class="indeterminate">
+        <input type="radio" name="group9" readonly class="indeterminate">
+        <input type="radio" name="group9" readonly class="indeterminate">
+        <input type="radio" name="group10" readonly class="indeterminate">
+        <span><input type="radio" name="group10" readonly class="indeterminate"></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly class="checked">
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly class="checked"></span>
+    </form>
+    <fieldset disabled class="disabled">
+        <input type="radio" class="indeterminate disabled">
+        <input type="radio" class="indeterminate disabled">
+        <input type="radio" name="group1" class="indeterminate disabled">
+        <input type="radio" name="group1" class="indeterminate disabled">
+        <input type="radio" name="group2" class="indeterminate disabled">
+        <span><input type="radio" name="group2" class="indeterminate disabled"></span>
+        <input type="radio" name="group3" class="disabled">
+        <input type="radio" name="group3" checked class="checked disabled">
+        <input type="radio" name="group4" class="disabled">
+        <span><input type="radio" name="group4" checked class="checked disabled"></span>
+        <input type="radio" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group5" disabled class="indeterminate disabled">
+        <input type="radio" name="group6" disabled class="indeterminate disabled">
+        <span><input type="radio" name="group6" disabled class="indeterminate disabled"></span>
+        <input type="radio" name="group7" disabled class="disabled">
+        <input type="radio" name="group7" checked disabled class="checked disabled">
+        <input type="radio" name="group8" disabled class="disabled">
+        <span><input type="radio" name="group8" checked disabled class="checked disabled"></span>
+        <input type="radio" readonly class="indeterminate disabled">
+        <input type="radio" name="group9" readonly class="indeterminate disabled">
+        <input type="radio" name="group9" readonly class="indeterminate disabled">
+        <input type="radio" name="group10" readonly class="indeterminate disabled">
+        <span><input type="radio" name="group10" readonly class="indeterminate disabled"></span>
+        <input type="radio" name="group11" readonly class="disabled">
+        <input type="radio" name="group11" checked readonly class="checked disabled">
+        <input type="radio" name="group12" readonly class="disabled">
+        <span><input type="radio" name="group12" checked readonly class="checked disabled"></span>
+    </fieldset>
+    <form>
+        <fieldset disabled class="disabled">
+            <input type="radio" class="indeterminate disabled">
+            <input type="radio" class="indeterminate disabled">
+            <input type="radio" name="group1" class="indeterminate disabled">
+            <input type="radio" name="group1" class="indeterminate disabled">
+            <input type="radio" name="group2" class="indeterminate disabled">
+            <span><input type="radio" name="group2" class="indeterminate disabled"></span>
+            <input type="radio" name="group3" class="disabled">
+            <input type="radio" name="group3" checked class="disabled">
+            <input type="radio" name="group4" class="disabled">
+            <span><input type="radio" name="group4" checked class="disabled"></span>
+            <input type="radio" disabled class="indeterminate disabled">
+            <input type="radio" name="group5" disabled class="indeterminate disabled">
+            <input type="radio" name="group5" disabled class="indeterminate disabled">
+            <input type="radio" name="group6" disabled class="indeterminate disabled">
+            <span><input type="radio" name="group6" disabled class="indeterminate disabled"></span>
+            <input type="radio" name="group7" disabled class="disabled">
+            <input type="radio" name="group7" checked disabled class="disabled">
+            <input type="radio" name="group8" disabled class="disabled">
+            <span><input type="radio" name="group8" checked disabled class="disabled"></span>
+            <input type="radio" readonly class="indeterminate disabled">
+            <input type="radio" name="group9" readonly class="indeterminate disabled">
+            <input type="radio" name="group9" readonly class="indeterminate disabled">
+            <input type="radio" name="group10" readonly class="indeterminate disabled">
+            <span><input type="radio" name="group10" readonly class="indeterminate disabled"></span>
+            <input type="radio" name="group11" readonly class="disabled">
+            <input type="radio" name="group11" checked readonly class="disabled">
+            <input type="radio" name="group12" readonly class="disabled">
+            <span><input type="radio" name="group12" checked readonly class="disabled"></span>
+        </fieldset>
+        <fieldset>
+            <input type="radio" class="indeterminate">
+            <input type="radio" class="indeterminate">
+            <input type="radio" name="group1" class="indeterminate">
+            <input type="radio" name="group1" class="indeterminate">
+            <input type="radio" name="group2" class="indeterminate">
+            <span><input type="radio" name="group2" class="indeterminate"></span>
+            <input type="radio" name="group3">
+            <input type="radio" name="group3" checked class="checked">
+            <input type="radio" name="group4">
+            <span><input type="radio" name="group4" checked class="checked"></span>
+            <input type="radio" disabled class="indeterminate disabled">
+            <input type="radio" name="group5" disabled class="indeterminate disabled">
+            <input type="radio" name="group5" disabled class="indeterminate disabled">
+            <input type="radio" name="group6" disabled class="indeterminate disabled">
+            <span><input type="radio" name="group6" disabled class="indeterminate disabled"></span>
+            <input type="radio" name="group7" disabled class="disabled">
+            <input type="radio" name="group7" checked disabled class="checked disabled">
+            <input type="radio" name="group8" disabled class="disabled">
+            <span><input type="radio" name="group8" checked disabled class="checked disabled"></span>
+            <input type="radio" readonly class="indeterminate">
+            <input type="radio" name="group9" readonly class="indeterminate">
+            <input type="radio" name="group9" readonly class="indeterminate">
+            <input type="radio" name="group10" readonly class="indeterminate">
+            <span><input type="radio" name="group10" readonly class="indeterminate"></span>
+            <input type="radio" name="group11" readonly>
+            <input type="radio" name="group11" checked readonly class="checked">
+            <input type="radio" name="group12" readonly>
+            <span><input type="radio" name="group12" checked readonly class="checked"></span>
+        </fieldset>
+    </form>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-indeterminate-radio-buttons-basics.html b/LayoutTests/fast/css/pseudo-indeterminate-radio-buttons-basics.html
new file mode 100644 (file)
index 0000000..9bfe63b
--- /dev/null
@@ -0,0 +1,206 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+    input {
+        background-color: blue;
+        width: 15px;
+        height: 15px;
+        -webkit-appearance: none;
+    }
+    :checked {
+        border: 2px solid red;
+    }
+    :indeterminate {
+        background-color: green;
+    }
+    :disabled {
+        border-left: 3px solid orange;
+    }
+</style>
+</head>
+<body>
+    <div>
+        <input type="radio">
+        <input type="radio">
+        <input type="radio" name="group1">
+        <input type="radio" name="group1">
+        <input type="radio" name="group2">
+        <span><input type="radio" name="group2"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked>
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked></span>
+        <input type="radio" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group6" disabled>
+        <span><input type="radio" name="group6" disabled></span>
+        <input type="radio" name="group7" disabled>
+        <input type="radio" name="group7" checked disabled>
+        <input type="radio" name="group8" disabled>
+        <span><input type="radio" name="group8" checked disabled></span>
+        <input type="radio" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group10" readonly>
+        <span><input type="radio" name="group10" readonly></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly>
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly></span>
+    </div>
+    <form>
+        <input type="radio">
+        <input type="radio">
+        <input type="radio" name="group1">
+        <input type="radio" name="group1">
+        <input type="radio" name="group2">
+        <span><input type="radio" name="group2"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked>
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked></span>
+        <input type="radio" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group6" disabled>
+        <span><input type="radio" name="group6" disabled></span>
+        <input type="radio" name="group7" disabled>
+        <input type="radio" name="group7" checked disabled>
+        <input type="radio" name="group8" disabled>
+        <span><input type="radio" name="group8" checked disabled></span>
+        <input type="radio" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group10" readonly>
+        <span><input type="radio" name="group10" readonly></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly>
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly></span>
+    </form>
+    <form disabled>
+        <input type="radio">
+        <input type="radio">
+        <input type="radio" name="group1">
+        <input type="radio" name="group1">
+        <input type="radio" name="group2">
+        <span><input type="radio" name="group2"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked>
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked></span>
+        <input type="radio" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group6" disabled>
+        <span><input type="radio" name="group6" disabled></span>
+        <input type="radio" name="group7" disabled>
+        <input type="radio" name="group7" checked disabled>
+        <input type="radio" name="group8" disabled>
+        <span><input type="radio" name="group8" checked disabled></span>
+        <input type="radio" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group10" readonly>
+        <span><input type="radio" name="group10" readonly></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly>
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly></span>
+    </form>
+    <fieldset disabled>
+        <input type="radio">
+        <input type="radio">
+        <input type="radio" name="group1">
+        <input type="radio" name="group1">
+        <input type="radio" name="group2">
+        <span><input type="radio" name="group2"></span>
+        <input type="radio" name="group3">
+        <input type="radio" name="group3" checked>
+        <input type="radio" name="group4">
+        <span><input type="radio" name="group4" checked></span>
+        <input type="radio" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group5" disabled>
+        <input type="radio" name="group6" disabled>
+        <span><input type="radio" name="group6" disabled></span>
+        <input type="radio" name="group7" disabled>
+        <input type="radio" name="group7" checked disabled>
+        <input type="radio" name="group8" disabled>
+        <span><input type="radio" name="group8" checked disabled></span>
+        <input type="radio" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group9" readonly>
+        <input type="radio" name="group10" readonly>
+        <span><input type="radio" name="group10" readonly></span>
+        <input type="radio" name="group11" readonly>
+        <input type="radio" name="group11" checked readonly>
+        <input type="radio" name="group12" readonly>
+        <span><input type="radio" name="group12" checked readonly></span>
+    </fieldset>
+    <form>
+        <fieldset disabled>
+            <input type="radio">
+            <input type="radio">
+            <input type="radio" name="group1">
+            <input type="radio" name="group1">
+            <input type="radio" name="group2">
+            <span><input type="radio" name="group2"></span>
+            <input type="radio" name="group3">
+            <input type="radio" name="group3" checked>
+            <input type="radio" name="group4">
+            <span><input type="radio" name="group4" checked></span>
+            <input type="radio" disabled>
+            <input type="radio" name="group5" disabled>
+            <input type="radio" name="group5" disabled>
+            <input type="radio" name="group6" disabled>
+            <span><input type="radio" name="group6" disabled></span>
+            <input type="radio" name="group7" disabled>
+            <input type="radio" name="group7" checked disabled>
+            <input type="radio" name="group8" disabled>
+            <span><input type="radio" name="group8" checked disabled></span>
+            <input type="radio" readonly>
+            <input type="radio" name="group9" readonly>
+            <input type="radio" name="group9" readonly>
+            <input type="radio" name="group10" readonly>
+            <span><input type="radio" name="group10" readonly></span>
+            <input type="radio" name="group11" readonly>
+            <input type="radio" name="group11" checked readonly>
+            <input type="radio" name="group12" readonly>
+            <span><input type="radio" name="group12" checked readonly></span>
+        </fieldset>
+        <fieldset>
+            <input type="radio">
+            <input type="radio">
+            <input type="radio" name="group1">
+            <input type="radio" name="group1">
+            <input type="radio" name="group2">
+            <span><input type="radio" name="group2"></span>
+            <input type="radio" name="group3">
+            <input type="radio" name="group3" checked>
+            <input type="radio" name="group4">
+            <span><input type="radio" name="group4" checked></span>
+            <input type="radio" disabled>
+            <input type="radio" name="group5" disabled>
+            <input type="radio" name="group5" disabled>
+            <input type="radio" name="group6" disabled>
+            <span><input type="radio" name="group6" disabled></span>
+            <input type="radio" name="group7" disabled>
+            <input type="radio" name="group7" checked disabled>
+            <input type="radio" name="group8" disabled>
+            <span><input type="radio" name="group8" checked disabled></span>
+            <input type="radio" readonly>
+            <input type="radio" name="group9" readonly>
+            <input type="radio" name="group9" readonly>
+            <input type="radio" name="group10" readonly>
+            <span><input type="radio" name="group10" readonly></span>
+            <input type="radio" name="group11" readonly>
+            <input type="radio" name="group11" checked readonly>
+            <input type="radio" name="group12" readonly>
+            <span><input type="radio" name="group12" checked readonly></span>
+        </fieldset>
+    </form>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation-expected.txt b/LayoutTests/fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation-expected.txt
new file mode 100644 (file)
index 0000000..5ac2aa6
--- /dev/null
@@ -0,0 +1,28 @@
+Verify that we do not invalidate more than needed to satisfy :indeterminate
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS elementsNeedingStyleRecalc() is []
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio2", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is []
+Check radio3
+PASS elementsNeedingStyleRecalc() is ["radio1", "radio2", "radio3", "radio4", "radio5"]
+PASS elementsWithIndeterminateStyle() is ["radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio3"]
+Check radio8
+PASS elementsNeedingStyleRecalc() is ["radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS elementsWithIndeterminateStyle() is []
+PASS checkedElements() is ["radio3", "radio8"]
+Check radio4
+PASS elementsNeedingStyleRecalc() is ["radio3", "radio4"]
+PASS elementsWithIndeterminateStyle() is []
+PASS checkedElements() is ["radio4", "radio8"]
+Check radio9
+PASS elementsNeedingStyleRecalc() is ["radio8", "radio9"]
+PASS elementsWithIndeterminateStyle() is []
+PASS checkedElements() is ["radio4", "radio9"]
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html b/LayoutTests/fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html
new file mode 100644 (file)
index 0000000..b5ac734
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+    input {
+        background-color: rgb(1, 2, 3);
+    }
+    :indeterminate {
+        background-color: rgb(4, 5, 6);
+    }
+</style>
+</head>
+<body>
+    <div id="with-renderer">
+        <!-- With renderer -->
+        <input type="radio" name="group1" id="radio1">
+        <input type="radio" name="group1" id="radio2">
+        <input type="radio" name="group1" id="radio3">
+        <input type="radio" name="group1" id="radio4">
+        <input type="radio" name="group1" id="radio5">
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <input type="radio" name="group2" id="radio6">
+        <input type="radio" name="group2" id="radio7">
+        <input type="radio" name="group2" id="radio8">
+        <input type="radio" name="group2" id="radio9">
+        <input type="radio" name="group2" id="radio10">
+    </div>
+</body>
+<script>
+
+description('Verify that we do not invalidate more than needed to satisfy :indeterminate');
+let allInputs = document.querySelectorAll("input");
+
+function elementsNeedingStyleRecalc() {
+    let elementsRequiringNeedStyleRecalc = []
+    for (let inputElement of allInputs) {
+        let needsStyleRecalc = window.internals.nodeNeedsStyleRecalc(inputElement);
+        if (needsStyleRecalc)
+            elementsRequiringNeedStyleRecalc.push(inputElement.id);
+    }
+    return elementsRequiringNeedStyleRecalc;
+}
+
+function elementsWithIndeterminateStyle() {
+    let elements = [];
+    for (let inputElement of allInputs) {
+        let backgroundColor = getComputedStyle(inputElement).backgroundColor;
+        if (backgroundColor === "rgb(4, 5, 6)")
+            elements.push(inputElement.id);
+    }
+    return elements;
+}
+
+function checkedElements() {
+    let elements = [];
+    for (let inputElement of allInputs) {
+        if (inputElement.checked)
+            elements.push(inputElement.id);
+    }
+    return elements;
+}
+
+// Force a layout to ensure we don't have dirty styles.
+var offsetTop = document.documentElement.offsetTop;
+
+// Initial state.
+shouldBe("elementsNeedingStyleRecalc()", '[]');
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio2", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '[]');
+
+// Check radio3. All the group1 require style recalc.
+debug("Check radio3");
+document.getElementById("radio3").checked = true;
+shouldBe("elementsNeedingStyleRecalc()", '["radio1", "radio2", "radio3", "radio4", "radio5"]');
+shouldBe("elementsWithIndeterminateStyle()", '["radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio3"]');
+
+// Check radio8. All the group1 require style recalc.
+debug("Check radio8");
+document.getElementById("radio8").checked = true;
+shouldBe("elementsNeedingStyleRecalc()", '["radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("elementsWithIndeterminateStyle()", '[]');
+shouldBe("checkedElements()", '["radio3", "radio8"]');
+
+// Checking radio4, we should not need to invalidate anything but the two modified elements.
+debug("Check radio4");
+document.getElementById("radio4").checked = true;
+shouldBe("elementsNeedingStyleRecalc()", '["radio3", "radio4"]');
+shouldBe("elementsWithIndeterminateStyle()", '[]');
+shouldBe("checkedElements()", '["radio4", "radio8"]');
+
+// Checking radio9, we should not need to invalidate anything but the two modified elements.
+debug("Check radio9");
+document.getElementById("radio9").checked = true;
+shouldBe("elementsNeedingStyleRecalc()", '["radio8", "radio9"]');
+shouldBe("elementsWithIndeterminateStyle()", '[]');
+shouldBe("checkedElements()", '["radio4", "radio9"]');
+
+// Hide the elements to make the results prettier.
+document.getElementById("with-renderer").style.display = "none";
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
index 2c29d1b..8aa140e 100644 (file)
@@ -27,9 +27,6 @@
     {
         try
         {
-
-        document.getElementsByTagName("input")[0].indeterminate = true;
-
         function ArrayContains(array, value, ci)
         {
             ci = ci == true ? true : false;
             }
             return false;
         }
+
         var target = document.getElementById("test");
         var val = getComputedStyle(target, null).getPropertyValue("color");
-        var aExpectedValues = new Array("green", "#008000", "rgb(0, 128, 0)");
+        var aExpectedValues = new Array("red", "#FF0000", "rgb(255, 0, 0)");
+        let wasIndeterminate = ArrayContains(aExpectedValues, val, true);
+
+        document.getElementsByTagName("input")[0].indeterminate = true;
 
-        if (ArrayContains(aExpectedValues, val, true))
+        if (wasIndeterminate && ArrayContains(aExpectedValues, val, true))
         {
 
           document.getElementById("testresult").innerHTML = "Pass";
@@ -66,4 +67,4 @@
 
 
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/LayoutTests/fast/selectors/detached-radio-button-checked-and-indeterminate-states-expected.txt b/LayoutTests/fast/selectors/detached-radio-button-checked-and-indeterminate-states-expected.txt
new file mode 100644 (file)
index 0000000..9503d56
--- /dev/null
@@ -0,0 +1,148 @@
+Verify :indeterminate, :checked and the indeterminate property on radio button detached from the document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initial state
+PASS document.getElementById('radio1-in-document').indeterminate is false
+PASS document.getElementById('radio2-in-document').indeterminate is false
+PASS document.getElementById('radio3-in-document').indeterminate is false
+PASS document.getElementById('radio4-in-document').indeterminate is false
+PASS document.getElementById('radio1-in-document').checked is false
+PASS document.getElementById('radio2-in-document').checked is true
+PASS document.getElementById('radio3-in-document').checked is false
+PASS document.getElementById('radio4-in-document').checked is false
+PASS document.getElementById('radio1-in-document').matches(':indeterminate') is false
+PASS document.getElementById('radio2-in-document').matches(':indeterminate') is false
+PASS document.getElementById('radio3-in-document').matches(':indeterminate') is true
+PASS document.getElementById('radio4-in-document').matches(':indeterminate') is true
+PASS document.getElementById('radio1-in-document').matches(':checked') is false
+PASS document.getElementById('radio2-in-document').matches(':checked') is true
+PASS document.getElementById('radio3-in-document').matches(':checked') is false
+PASS document.getElementById('radio4-in-document').matches(':checked') is false
+PASS getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById('radio2-in-document')).backgroundColor is "rgb(7, 8, 9)"
+PASS getComputedStyle(document.getElementById('radio3-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById('radio1-in-document')).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById('radio2-in-document')).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.getElementById('radio3-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(document.getElementById('radio4-in-document')).color is "rgb(10, 11, 12)"
+Remove radio2-in-document from document, into variable radio2
+PASS document.querySelectorAll('radio2-in-document') is []
+PASS document.getElementById('radio1-in-document').indeterminate is false
+PASS radio2.indeterminate is false
+PASS document.getElementById('radio3-in-document').indeterminate is false
+PASS document.getElementById('radio4-in-document').indeterminate is false
+PASS document.getElementById('radio1-in-document').checked is false
+PASS radio2.checked is true
+PASS document.getElementById('radio3-in-document').checked is false
+PASS document.getElementById('radio4-in-document').checked is false
+PASS document.getElementById('radio1-in-document').matches(':indeterminate') is true
+PASS radio2.matches(':indeterminate') is false
+PASS document.getElementById('radio3-in-document').matches(':indeterminate') is true
+PASS document.getElementById('radio4-in-document').matches(':indeterminate') is true
+PASS document.getElementById('radio1-in-document').matches(':checked') is false
+PASS radio2.matches(':checked') is true
+PASS document.getElementById('radio3-in-document').matches(':checked') is false
+PASS document.getElementById('radio4-in-document').matches(':checked') is false
+PASS getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(radio2).backgroundColor is ""
+PASS getComputedStyle(document.getElementById('radio3-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById('radio1-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(radio2).color is ""
+PASS getComputedStyle(document.getElementById('radio3-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(document.getElementById('radio4-in-document')).color is "rgb(10, 11, 12)"
+Remove radio3-in-document from document, into variable radio3
+PASS document.querySelectorAll('radio3-in-document') is []
+PASS document.getElementById('radio1-in-document').indeterminate is false
+PASS radio2.indeterminate is false
+PASS radio3.indeterminate is false
+PASS document.getElementById('radio4-in-document').indeterminate is false
+PASS document.getElementById('radio1-in-document').checked is false
+PASS radio2.checked is true
+PASS radio3.checked is false
+PASS document.getElementById('radio4-in-document').checked is false
+PASS document.getElementById('radio1-in-document').matches(':indeterminate') is true
+PASS radio2.matches(':indeterminate') is false
+PASS radio3.matches(':indeterminate') is true
+PASS document.getElementById('radio4-in-document').matches(':indeterminate') is true
+PASS document.getElementById('radio1-in-document').matches(':checked') is false
+PASS radio2.matches(':checked') is true
+PASS radio3.matches(':checked') is false
+PASS document.getElementById('radio4-in-document').matches(':checked') is false
+PASS getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(radio2).backgroundColor is ""
+PASS getComputedStyle(radio3).backgroundColor is ""
+PASS getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.getElementById('radio1-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(radio2).color is ""
+PASS getComputedStyle(radio3).color is ""
+PASS getComputedStyle(document.getElementById('radio4-in-document')).color is "rgb(10, 11, 12)"
+Create new element named webkitRadio
+PASS document.getElementById('radio1-in-document').indeterminate is false
+PASS radio2.indeterminate is false
+PASS radio3.indeterminate is false
+PASS document.getElementById('radio4-in-document').indeterminate is false
+PASS webkitRadio.indeterminate is false
+PASS document.getElementById('radio1-in-document').checked is false
+PASS radio2.checked is true
+PASS radio3.checked is false
+PASS document.getElementById('radio4-in-document').checked is false
+PASS webkitRadio.checked is false
+PASS document.getElementById('radio1-in-document').matches(':indeterminate') is true
+PASS radio2.matches(':indeterminate') is false
+PASS radio3.matches(':indeterminate') is true
+PASS document.getElementById('radio4-in-document').matches(':indeterminate') is true
+PASS webkitRadio.matches(':indeterminate') is true
+PASS document.getElementById('radio1-in-document').matches(':checked') is false
+PASS radio2.matches(':checked') is true
+PASS radio3.matches(':checked') is false
+PASS document.getElementById('radio4-in-document').matches(':checked') is false
+PASS webkitRadio.matches(':checked') is false
+PASS getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(radio2).backgroundColor is ""
+PASS getComputedStyle(radio3).backgroundColor is ""
+PASS getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(webkitRadio).backgroundColor is ""
+PASS getComputedStyle(document.getElementById('radio1-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(radio2).color is ""
+PASS getComputedStyle(radio3).color is ""
+PASS getComputedStyle(document.getElementById('radio4-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(webkitRadio).color is ""
+Check webkitRadio
+PASS document.getElementById('radio1-in-document').indeterminate is false
+PASS radio2.indeterminate is false
+PASS radio3.indeterminate is false
+PASS document.getElementById('radio4-in-document').indeterminate is false
+PASS webkitRadio.indeterminate is false
+PASS document.getElementById('radio1-in-document').checked is false
+PASS radio2.checked is true
+PASS radio3.checked is false
+PASS document.getElementById('radio4-in-document').checked is false
+PASS webkitRadio.checked is true
+PASS document.getElementById('radio1-in-document').matches(':indeterminate') is true
+PASS radio2.matches(':indeterminate') is false
+PASS radio3.matches(':indeterminate') is true
+PASS document.getElementById('radio4-in-document').matches(':indeterminate') is true
+PASS webkitRadio.matches(':indeterminate') is false
+PASS document.getElementById('radio1-in-document').matches(':checked') is false
+PASS radio2.matches(':checked') is true
+PASS radio3.matches(':checked') is false
+PASS document.getElementById('radio4-in-document').matches(':checked') is false
+PASS webkitRadio.matches(':checked') is true
+PASS getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(radio2).backgroundColor is ""
+PASS getComputedStyle(radio3).backgroundColor is ""
+PASS getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(webkitRadio).backgroundColor is ""
+PASS getComputedStyle(document.getElementById('radio1-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(radio2).color is ""
+PASS getComputedStyle(radio3).color is ""
+PASS getComputedStyle(document.getElementById('radio4-in-document')).color is "rgb(10, 11, 12)"
+PASS getComputedStyle(webkitRadio).color is ""
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/detached-radio-button-checked-and-indeterminate-states.html b/LayoutTests/fast/selectors/detached-radio-button-checked-and-indeterminate-states.html
new file mode 100644 (file)
index 0000000..71591cc
--- /dev/null
@@ -0,0 +1,208 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+    input {
+        background-color: rgb(1, 2, 3);
+        color: rgb(4, 5, 6);
+    }
+    :checked {
+        background-color: rgb(7, 8, 9);
+    }
+    :indeterminate {
+        color: rgb(10, 11, 12);
+    }
+</style>
+</head>
+<body>
+    <div id="testcases">
+        <input type="radio" name="group1" id="radio1-in-document">
+        <input type="radio" name="group1" id="radio2-in-document" checked>
+        <input type="radio" name="group2" id="radio3-in-document">
+        <input type="radio" name="group2" id="radio4-in-document">
+    </div>
+</body>
+<script>
+description('Verify :indeterminate, :checked and the indeterminate property on radio button detached from the document.');
+
+debug("Initial state");
+shouldBeFalse("document.getElementById('radio1-in-document').indeterminate");
+shouldBeFalse("document.getElementById('radio2-in-document').indeterminate");
+shouldBeFalse("document.getElementById('radio3-in-document').indeterminate");
+shouldBeFalse("document.getElementById('radio4-in-document').indeterminate");
+
+shouldBeFalse("document.getElementById('radio1-in-document').checked");
+shouldBeTrue("document.getElementById('radio2-in-document').checked");
+shouldBeFalse("document.getElementById('radio3-in-document').checked");
+shouldBeFalse("document.getElementById('radio4-in-document').checked");
+
+shouldBeFalse("document.getElementById('radio1-in-document').matches(':indeterminate')");
+shouldBeFalse("document.getElementById('radio2-in-document').matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio3-in-document').matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio4-in-document').matches(':indeterminate')");
+
+shouldBeFalse("document.getElementById('radio1-in-document').matches(':checked')");
+shouldBeTrue("document.getElementById('radio2-in-document').matches(':checked')");
+shouldBeFalse("document.getElementById('radio3-in-document').matches(':checked')");
+shouldBeFalse("document.getElementById('radio4-in-document').matches(':checked')");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio2-in-document')).backgroundColor", "rgb(7, 8, 9)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio3-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor", "rgb(1, 2, 3)");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).color", "rgb(4, 5, 6)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio2-in-document')).color", "rgb(4, 5, 6)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio3-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).color", "rgb(10, 11, 12)");
+
+debug("Remove radio2-in-document from document, into variable radio2");
+let radio2 = document.getElementById('radio2-in-document');
+radio2.parentElement.removeChild(radio2);
+shouldBe("document.querySelectorAll('radio2-in-document')", "[]");
+shouldBeFalse("document.getElementById('radio1-in-document').indeterminate");
+shouldBeFalse("radio2.indeterminate");
+shouldBeFalse("document.getElementById('radio3-in-document').indeterminate");
+shouldBeFalse("document.getElementById('radio4-in-document').indeterminate");
+
+shouldBeFalse("document.getElementById('radio1-in-document').checked");
+shouldBeTrue("radio2.checked");
+shouldBeFalse("document.getElementById('radio3-in-document').checked");
+shouldBeFalse("document.getElementById('radio4-in-document').checked");
+
+shouldBeTrue("document.getElementById('radio1-in-document').matches(':indeterminate')");
+shouldBeFalse("radio2.matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio3-in-document').matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio4-in-document').matches(':indeterminate')");
+
+shouldBeFalse("document.getElementById('radio1-in-document').matches(':checked')");
+shouldBeTrue("radio2.matches(':checked')");
+shouldBeFalse("document.getElementById('radio3-in-document').matches(':checked')");
+shouldBeFalse("document.getElementById('radio4-in-document').matches(':checked')");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(radio2).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio3-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor", "rgb(1, 2, 3)");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(radio2).color", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio3-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).color", "rgb(10, 11, 12)");
+
+debug("Remove radio3-in-document from document, into variable radio3");
+let radio3 = document.getElementById('radio3-in-document');
+radio3.parentElement.removeChild(radio3);
+shouldBe("document.querySelectorAll('radio3-in-document')", "[]");
+shouldBeFalse("document.getElementById('radio1-in-document').indeterminate");
+shouldBeFalse("radio2.indeterminate");
+shouldBeFalse("radio3.indeterminate");
+shouldBeFalse("document.getElementById('radio4-in-document').indeterminate");
+
+shouldBeFalse("document.getElementById('radio1-in-document').checked");
+shouldBeTrue("radio2.checked");
+shouldBeFalse("radio3.checked");
+shouldBeFalse("document.getElementById('radio4-in-document').checked");
+
+shouldBeTrue("document.getElementById('radio1-in-document').matches(':indeterminate')");
+shouldBeFalse("radio2.matches(':indeterminate')");
+shouldBeTrue("radio3.matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio4-in-document').matches(':indeterminate')");
+
+shouldBeFalse("document.getElementById('radio1-in-document').matches(':checked')");
+shouldBeTrue("radio2.matches(':checked')");
+shouldBeFalse("radio3.matches(':checked')");
+shouldBeFalse("document.getElementById('radio4-in-document').matches(':checked')");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(radio2).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(radio3).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor", "rgb(1, 2, 3)");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(radio2).color", "");
+shouldBeEqualToString("getComputedStyle(radio3).color", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).color", "rgb(10, 11, 12)");
+
+debug("Create new element named webkitRadio");
+let webkitRadio = document.createElement("input");
+webkitRadio.type = "radio";
+shouldBeFalse("document.getElementById('radio1-in-document').indeterminate");
+shouldBeFalse("radio2.indeterminate");
+shouldBeFalse("radio3.indeterminate");
+shouldBeFalse("document.getElementById('radio4-in-document').indeterminate");
+shouldBeFalse("webkitRadio.indeterminate");
+
+shouldBeFalse("document.getElementById('radio1-in-document').checked");
+shouldBeTrue("radio2.checked");
+shouldBeFalse("radio3.checked");
+shouldBeFalse("document.getElementById('radio4-in-document').checked");
+shouldBeFalse("webkitRadio.checked");
+
+shouldBeTrue("document.getElementById('radio1-in-document').matches(':indeterminate')");
+shouldBeFalse("radio2.matches(':indeterminate')");
+shouldBeTrue("radio3.matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio4-in-document').matches(':indeterminate')");
+shouldBeTrue("webkitRadio.matches(':indeterminate')");
+
+shouldBeFalse("document.getElementById('radio1-in-document').matches(':checked')");
+shouldBeTrue("radio2.matches(':checked')");
+shouldBeFalse("radio3.matches(':checked')");
+shouldBeFalse("document.getElementById('radio4-in-document').matches(':checked')");
+shouldBeFalse("webkitRadio.matches(':checked')");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(radio2).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(radio3).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(webkitRadio).backgroundColor", "");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(radio2).color", "");
+shouldBeEqualToString("getComputedStyle(radio3).color", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(webkitRadio).color", "");
+
+debug("Check webkitRadio");
+webkitRadio.checked = true;
+shouldBeFalse("document.getElementById('radio1-in-document').indeterminate");
+shouldBeFalse("radio2.indeterminate");
+shouldBeFalse("radio3.indeterminate");
+shouldBeFalse("document.getElementById('radio4-in-document').indeterminate");
+shouldBeFalse("webkitRadio.indeterminate");
+
+shouldBeFalse("document.getElementById('radio1-in-document').checked");
+shouldBeTrue("radio2.checked");
+shouldBeFalse("radio3.checked");
+shouldBeFalse("document.getElementById('radio4-in-document').checked");
+shouldBeTrue("webkitRadio.checked");
+
+shouldBeTrue("document.getElementById('radio1-in-document').matches(':indeterminate')");
+shouldBeFalse("radio2.matches(':indeterminate')");
+shouldBeTrue("radio3.matches(':indeterminate')");
+shouldBeTrue("document.getElementById('radio4-in-document').matches(':indeterminate')");
+shouldBeFalse("webkitRadio.matches(':indeterminate')");
+
+shouldBeFalse("document.getElementById('radio1-in-document').matches(':checked')");
+shouldBeTrue("radio2.matches(':checked')");
+shouldBeFalse("radio3.matches(':checked')");
+shouldBeFalse("document.getElementById('radio4-in-document').matches(':checked')");
+shouldBeTrue("webkitRadio.matches(':checked')");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(radio2).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(radio3).backgroundColor", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).backgroundColor", "rgb(1, 2, 3)");
+shouldBeEqualToString("getComputedStyle(webkitRadio).backgroundColor", "");
+
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio1-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(radio2).color", "");
+shouldBeEqualToString("getComputedStyle(radio3).color", "");
+shouldBeEqualToString("getComputedStyle(document.getElementById('radio4-in-document')).color", "rgb(10, 11, 12)");
+shouldBeEqualToString("getComputedStyle(webkitRadio).color", "");
+
+document.getElementById("testcases").style.display = "none";
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
diff --git a/LayoutTests/fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update-expected.txt b/LayoutTests/fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update-expected.txt
new file mode 100644 (file)
index 0000000..7534c1c
--- /dev/null
@@ -0,0 +1,59 @@
+Verify we invalidate radio button groups to match :indeterminate when a button is checked/unchecked
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio2", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is []
+Check radio3
+PASS elementsWithIndeterminateStyle() is ["radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio3"]
+Check radio8
+PASS elementsWithIndeterminateStyle() is []
+PASS checkedElements() is ["radio3", "radio8"]
+Check radio4
+PASS elementsWithIndeterminateStyle() is []
+PASS checkedElements() is ["radio4", "radio8"]
+Check radio9
+PASS elementsWithIndeterminateStyle() is []
+PASS checkedElements() is ["radio4", "radio9"]
+Uncheck radio4
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio2", "radio3", "radio4", "radio5"]
+PASS checkedElements() is ["radio9"]
+Uncheck radio9
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio2", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is []
+Check radio1
+PASS elementsWithIndeterminateStyle() is ["radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio1"]
+Check radio2
+PASS elementsWithIndeterminateStyle() is ["radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio2"]
+Remove radio3 from its group
+PASS elementsWithIndeterminateStyle() is ["radio3", "radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio2"]
+Remove radio6 from its group
+PASS elementsWithIndeterminateStyle() is ["radio3", "radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio2"]
+Remove radio2 from its group
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]
+PASS checkedElements() is ["radio2"]
+Check radio7
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio3", "radio4", "radio5", "radio6"]
+PASS checkedElements() is ["radio2", "radio7"]
+Check radio8
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio3", "radio4", "radio5", "radio6"]
+PASS checkedElements() is ["radio2", "radio8"]
+Remove radio9 from its group
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio3", "radio4", "radio5", "radio6", "radio9"]
+PASS checkedElements() is ["radio2", "radio8"]
+Remove radio8 from its group
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio3", "radio4", "radio5", "radio6", "radio7", "radio9", "radio10"]
+PASS checkedElements() is ["radio2", "radio8"]
+Remove radio7 from its group
+PASS elementsWithIndeterminateStyle() is ["radio1", "radio3", "radio4", "radio5", "radio6", "radio7", "radio9", "radio10"]
+PASS checkedElements() is ["radio2", "radio8"]
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html b/LayoutTests/fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html
new file mode 100644 (file)
index 0000000..458dae7
--- /dev/null
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+    input {
+        background-color: rgb(1, 2, 3);
+    }
+    :indeterminate {
+        background-color: rgb(4, 5, 6);
+    }
+</style>
+</head>
+<body>
+    <div id="with-renderer">
+        <!-- With renderer -->
+        <input type="radio" name="group1" id="radio1">
+        <input type="radio" name="group1" id="radio2">
+        <input type="radio" name="group1" id="radio3">
+        <span>
+            <input type="radio" name="group1" id="radio4">
+            <input type="radio" name="group1" id="radio5">
+        </span>
+    </div>
+    <div style="display:none;">
+        <!-- Without renderer -->
+        <input type="radio" name="group2" id="radio6">
+        <span>
+            <input type="radio" name="group2" id="radio7">
+            <input type="radio" name="group2" id="radio8">
+        </span>
+        <input type="radio" name="group2" id="radio9">
+        <input type="radio" name="group2" id="radio10">
+    </div>
+</body>
+<script>
+
+description('Verify we invalidate radio button groups to match :indeterminate when a button is checked/unchecked');
+let allInputs = document.querySelectorAll("input");
+
+function elementsWithIndeterminateStyle() {
+    let elements = [];
+    for (let inputElement of allInputs) {
+        let backgroundColor = getComputedStyle(inputElement).backgroundColor;
+        if (backgroundColor === "rgb(4, 5, 6)")
+            elements.push(inputElement.id);
+    }
+    return elements;
+}
+
+function checkedElements() {
+    let elements = [];
+    for (let inputElement of allInputs) {
+        if (inputElement.checked)
+            elements.push(inputElement.id);
+    }
+    return elements;
+}
+
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio2", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '[]');
+
+debug("Check radio3");
+document.getElementById("radio3").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '["radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio3"]');
+
+debug("Check radio8");
+document.getElementById("radio8").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '[]');
+shouldBe("checkedElements()", '["radio3", "radio8"]');
+
+debug("Check radio4");
+document.getElementById("radio4").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '[]');
+shouldBe("checkedElements()", '["radio4", "radio8"]');
+
+debug("Check radio9");
+document.getElementById("radio9").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '[]');
+shouldBe("checkedElements()", '["radio4", "radio9"]');
+
+debug("Uncheck radio4");
+document.getElementById("radio4").checked = false;
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio2", "radio3", "radio4", "radio5"]');
+shouldBe("checkedElements()", '["radio9"]');
+
+debug("Uncheck radio9");
+document.getElementById("radio9").checked = false;
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio2", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '[]');
+
+debug("Check radio1");
+document.getElementById("radio1").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '["radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio1"]');
+
+debug("Check radio2");
+document.getElementById("radio2").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '["radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio2"]');
+
+debug("Remove radio3 from its group");
+document.getElementById("radio3").name = "";
+shouldBe("elementsWithIndeterminateStyle()", '["radio3", "radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio2"]');
+
+debug("Remove radio6 from its group");
+document.getElementById("radio6").name = "";
+shouldBe("elementsWithIndeterminateStyle()", '["radio3", "radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio2"]');
+
+debug("Remove radio2 from its group");
+document.getElementById("radio2").name = "";
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio3", "radio4", "radio5", "radio6", "radio7", "radio8", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio2"]');
+
+debug("Check radio7");
+document.getElementById("radio7").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio3", "radio4", "radio5", "radio6"]');
+shouldBe("checkedElements()", '["radio2", "radio7"]');
+
+debug("Check radio8");
+document.getElementById("radio8").checked = true;
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio3", "radio4", "radio5", "radio6"]');
+shouldBe("checkedElements()", '["radio2", "radio8"]');
+
+debug("Remove radio9 from its group");
+document.getElementById("radio9").name = "";
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio3", "radio4", "radio5", "radio6", "radio9"]');
+shouldBe("checkedElements()", '["radio2", "radio8"]');
+
+debug("Remove radio8 from its group");
+document.getElementById("radio8").name = "";
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio3", "radio4", "radio5", "radio6", "radio7", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio2", "radio8"]');
+
+debug("Remove radio7 from its group");
+document.getElementById("radio7").name = "";
+shouldBe("elementsWithIndeterminateStyle()", '["radio1", "radio3", "radio4", "radio5", "radio6", "radio7", "radio9", "radio10"]');
+shouldBe("checkedElements()", '["radio2", "radio8"]');
+
+
+// Hide the elements to make the results prettier.
+document.getElementById("with-renderer").style.display = "none";
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</html>
index 8c63e76..bb23d11 100644 (file)
@@ -1,3 +1,13 @@
+2016-06-17  Benjamin Poulain  <benjamin@webkit.org>
+
+        :indeterminate pseudo-class should match radios whose group has no checked radio
+        https://bugs.webkit.org/show_bug.cgi?id=156270
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/html/semantics/selectors/pseudo-classes/indeterminate-expected.txt:
+        One more pass on official tests :)
+
 2016-06-17  Youenn Fablet  <youenn.fablet@crf.canon.fr>
 
         CORS preflight with a non-200 response should be a preflight failure
index 33d80ea..cf7a53a 100644 (file)
@@ -1,7 +1,7 @@
         
 
-FAIL ':progress' matches <input>s radio buttons whose radio button group contains no checked input and <progress> elements without value attribute assert_array_equals: lengths differ, expected 5 got 1
-FAIL dynamically check a radio input in a radio button group assert_array_equals: lengths differ, expected 3 got 1
+PASS ':progress' matches <input>s radio buttons whose radio button group contains no checked input and <progress> elements without value attribute 
+PASS dynamically check a radio input in a radio button group 
 FAIL click on radio4 which is in the indeterminate state assert_array_equals: lengths differ, expected 2 got 1
 PASS adding a value to progress1 should put it in a determinate state 
 PASS removing progress2's value should put it in an indeterminate state 
diff --git a/LayoutTests/platform/ios-simulator/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-expected.txt b/LayoutTests/platform/ios-simulator/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-expected.txt
deleted file mode 100644 (file)
index f6c6073..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-FAIL :indeterminate and input type=radio assert_equals: The indeterminate IDL attribute should not cause the :indeterminate pseudo-class to match on input type=radio expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
-
index dfa41c9..fc4df75 100644 (file)
@@ -1,3 +1,79 @@
+2016-06-17  Benjamin Poulain  <benjamin@webkit.org>
+
+        :indeterminate pseudo-class should match radios whose group has no checked radio
+        https://bugs.webkit.org/show_bug.cgi?id=156270
+
+        Reviewed by Simon Fraser.
+
+        The pseudo-class ":indeterminate" is supposed to match radio buttons
+        for which the entire group has no checked button.
+        Spec: https://html.spec.whatwg.org/#pseudo-classes:selector-indeterminate
+
+        The change is straightforward with one non-obvious choice:
+        I added matchesIndeterminatePseudoClass() in addition to shouldAppearIndeterminate().
+
+        The reason is shouldAppearIndeterminate() is used for styling and AX of elements
+        with an indeterminate states (check boxes and progress element). There is no such
+        UI for radio boxes.
+        I could have extended shouldAppearIndeterminate() to radio box
+        then filter out this case in RenderTheme. The problem is doing that would also requires
+        changes to the repaint logic to match :indeterminate. It seemed overkill to me to
+        change repaint() for a case that is never used in practice.
+
+        Tests: fast/css/pseudo-indeterminate-radio-buttons-basics.html
+               fast/css/pseudo-indeterminate-with-radio-buttons-style-invalidation.html
+               fast/selectors/detached-radio-button-checked-and-indeterminate-states.html
+               fast/selectors/pseudo-indeterminate-with-radio-buttons-style-update.html
+
+        * css/SelectorCheckerTestFunctions.h:
+        (WebCore::shouldAppearIndeterminate):
+        * dom/Element.cpp:
+        (WebCore::Element::matchesIndeterminatePseudoClass):
+        * dom/Element.h:
+        * dom/RadioButtonGroups.cpp:
+        (WebCore::RadioButtonGroup::setCheckedButton):
+        (WebCore::RadioButtonGroup::updateCheckedState):
+        (WebCore::RadioButtonGroup::remove):
+        (WebCore::RadioButtonGroup::setNeedsStyleRecalcForAllButtons):
+        (WebCore::RadioButtonGroups::hasCheckedButton):
+        * dom/RadioButtonGroups.h:
+        * html/CheckboxInputType.cpp:
+        (WebCore::CheckboxInputType::matchesIndeterminatePseudoClass):
+        (WebCore::CheckboxInputType::shouldAppearIndeterminate):
+        (WebCore::CheckboxInputType::supportsIndeterminateAppearance): Deleted.
+        * html/CheckboxInputType.h:
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::setChecked):
+        (WebCore::HTMLInputElement::matchesIndeterminatePseudoClass):
+        (WebCore::HTMLInputElement::shouldAppearIndeterminate):
+        (WebCore::HTMLInputElement::radioButtonGroups):
+        * html/HTMLInputElement.h:
+        * html/InputType.cpp:
+        (WebCore::InputType::matchesIndeterminatePseudoClass):
+        (WebCore::InputType::shouldAppearIndeterminate):
+        (WebCore::InputType::supportsIndeterminateAppearance): Deleted.
+        * html/InputType.h:
+        * html/RadioInputType.cpp:
+        (WebCore::RadioInputType::matchesIndeterminatePseudoClass):
+        (WebCore::RadioInputType::willDispatchClick): Deleted.
+        (WebCore::RadioInputType::didDispatchClick): Deleted.
+        (WebCore::RadioInputType::supportsIndeterminateAppearance): Deleted.
+        The iOS specific code is just plain wrong.
+        It was changing the indeterminate state of the input element.
+        The spec clearly says that state is only used by checkbox:
+        https://html.spec.whatwg.org/#dom-input-indeterminate
+
+        Moreover, the style update would not change the indeterminate state
+        of other buttons in the Button Group, which is just bizarre.
+        RenderThemeIOS does not make use of any of this with the current style.
+
+        * html/RadioInputType.h:
+        * style/StyleSharingResolver.cpp:
+        (WebCore::Style::SharingResolver::canShareStyleWithElement):
+        (WebCore::Style::canShareStyleWithControl): Deleted.
+        (WebCore::Style::SharingResolver::sharingCandidateHasIdenticalStyleAffectingAttributes): Deleted.
+        Style sharing is unified behind the selector matching which is neat.
+
 2016-06-17  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r202152.
index 725ad4a..149190d 100644 (file)
@@ -983,7 +983,7 @@ bool SelectorChecker::checkOne(CheckingContext& checkingContext, const LocalCont
         case CSSSelector::PseudoClassChecked:
             return isChecked(element);
         case CSSSelector::PseudoClassIndeterminate:
-            return shouldAppearIndeterminate(element);
+            return matchesIndeterminatePseudoClass(element);
         case CSSSelector::PseudoClassRoot:
             if (&element == element.document().documentElement())
                 return true;
index c42ca01..4c76770 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  * Copyright (C) 2014 Dhi Aurrahman <diorahman@rockybars.com>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -221,9 +221,9 @@ ALWAYS_INLINE bool matchesReadWritePseudoClass(const Element& element)
     return element.matchesReadWritePseudoClass();
 }
 
-ALWAYS_INLINE bool shouldAppearIndeterminate(const Element& element)
+ALWAYS_INLINE bool matchesIndeterminatePseudoClass(const Element& element)
 {
-    return element.shouldAppearIndeterminate();
+    return element.matchesIndeterminatePseudoClass();
 }
 
 ALWAYS_INLINE bool scrollbarMatchesEnabledPseudoClass(const SelectorChecker::CheckingContext& context)
index 089e726..3fedf67 100644 (file)
@@ -558,7 +558,7 @@ static inline FunctionType addPseudoClassType(const CSSSelector& selector, Selec
         fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(isInRange));
         return FunctionType::SimpleSelectorChecker;
     case CSSSelector::PseudoClassIndeterminate:
-        fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(shouldAppearIndeterminate));
+        fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(matchesIndeterminatePseudoClass));
         return FunctionType::SimpleSelectorChecker;
     case CSSSelector::PseudoClassInvalid:
         fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(isInvalid));
index eebb715..2190eaa 100644 (file)
@@ -4,7 +4,7 @@
  *           (C) 2001 Peter Kelly (pmk@post.com)
  *           (C) 2001 Dirk Mueller (mueller@kde.org)
  *           (C) 2007 David Smith (catfish.man@gmail.com)
- * Copyright (C) 2004-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2016 Apple Inc. All rights reserved.
  *           (C) 2007 Eric Seidel (eric@webkit.org)
  *
  * This library is free software; you can redistribute it and/or
@@ -2755,6 +2755,11 @@ bool Element::matchesReadWritePseudoClass() const
     return false;
 }
 
+bool Element::matchesIndeterminatePseudoClass() const
+{
+    return shouldAppearIndeterminate();
+}
+
 bool Element::matches(const String& selector, ExceptionCode& ec)
 {
     SelectorQuery* selectorQuery = document().selectorQueryForString(selector, ec);
index 2faa64b..d149203 100644 (file)
@@ -3,7 +3,7 @@
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2001 Peter Kelly (pmk@post.com)
  *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -386,6 +386,7 @@ public:
     void didShadowTreeAwareChildrenChange();
 
     virtual bool matchesReadWritePseudoClass() const;
+    virtual bool matchesIndeterminatePseudoClass() const;
     bool matches(const String& selectors, ExceptionCode&);
     Element* closest(const String& selectors, ExceptionCode&);
     virtual bool shouldAppearIndeterminate() const;
index da575e0..c7dc0b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2009, 2016 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -42,8 +42,10 @@ public:
     Vector<HTMLInputElement*> members() const;
 
 private:
+    void setNeedsStyleRecalcForAllButtons();
     void updateValidityForAllButtons();
     bool isValid() const;
+    void changeCheckedButton(HTMLInputElement*);
     void setCheckedButton(HTMLInputElement*);
 
     HashSet<HTMLInputElement*> m_members;
@@ -75,6 +77,12 @@ void RadioButtonGroup::setCheckedButton(HTMLInputElement* button)
     HTMLInputElement* oldCheckedButton = m_checkedButton;
     if (oldCheckedButton == button)
         return;
+
+    bool hadCheckedButton = m_checkedButton;
+    bool willHaveCheckedButton = button;
+    if (hadCheckedButton != willHaveCheckedButton)
+        setNeedsStyleRecalcForAllButtons();
+
     m_checkedButton = button;
     if (oldCheckedButton)
         oldCheckedButton->setChecked(false);
@@ -110,7 +118,7 @@ void RadioButtonGroup::updateCheckedState(HTMLInputElement* button)
         setCheckedButton(button);
     else {
         if (m_checkedButton == button)
-            m_checkedButton = nullptr;
+            setCheckedButton(nullptr);
     }
     if (wasValid != isValid())
         updateValidityForAllButtons();
@@ -137,14 +145,20 @@ void RadioButtonGroup::remove(HTMLInputElement* button)
     HashSet<HTMLInputElement*>::iterator it = m_members.find(button);
     if (it == m_members.end())
         return;
+
     bool wasValid = isValid();
     m_members.remove(it);
     if (button->isRequired()) {
         ASSERT(m_requiredCount);
         --m_requiredCount;
     }
-    if (m_checkedButton == button)
-        m_checkedButton = nullptr;
+    if (m_checkedButton) {
+        button->setNeedsStyleRecalc();
+        if (m_checkedButton == button) {
+            m_checkedButton = nullptr;
+            setNeedsStyleRecalcForAllButtons();
+        }
+    }
 
     if (m_members.isEmpty()) {
         ASSERT(!m_requiredCount);
@@ -158,6 +172,14 @@ void RadioButtonGroup::remove(HTMLInputElement* button)
     }
 }
 
+void RadioButtonGroup::setNeedsStyleRecalcForAllButtons()
+{
+    for (auto& button : m_members) {
+        ASSERT(button->isRadioButton());
+        button->setNeedsStyleRecalc();
+    }
+}
+
 void RadioButtonGroup::updateValidityForAllButtons()
 {
     for (auto& button : m_members) {
@@ -250,6 +272,17 @@ HTMLInputElement* RadioButtonGroups::checkedButtonForGroup(const AtomicString& n
     return group ? group->checkedButton() : nullptr;
 }
 
+bool RadioButtonGroups::hasCheckedButton(const HTMLInputElement* element) const
+{
+    ASSERT(element->isRadioButton());
+    const AtomicString& name = element->name();
+    if (name.isEmpty() || !m_nameToGroupMap)
+        return element->checked();
+
+    const RadioButtonGroup* group = m_nameToGroupMap->get(name.impl());
+    return group->checkedButton();
+}
+
 bool RadioButtonGroups::isInRequiredGroup(HTMLInputElement* element) const
 {
     ASSERT(element->isRadioButton());
index 401b195..1e5a58d 100644 (file)
@@ -39,6 +39,7 @@ public:
     void requiredAttributeChanged(HTMLInputElement*);
     void removeButton(HTMLInputElement*);
     HTMLInputElement* checkedButtonForGroup(const AtomicString& groupName) const;
+    bool hasCheckedButton(const HTMLInputElement*) const;
     bool isInRequiredGroup(HTMLInputElement*) const;
     Vector<HTMLInputElement*> groupMembers(const HTMLInputElement&) const;
 
index 69cf05b..50c822f 100644 (file)
@@ -92,9 +92,14 @@ bool CheckboxInputType::isCheckbox() const
     return true;
 }
 
-bool CheckboxInputType::supportsIndeterminateAppearance() const
+bool CheckboxInputType::matchesIndeterminatePseudoClass() const
 {
-    return true;
+    return shouldAppearIndeterminate();
+}
+
+bool CheckboxInputType::shouldAppearIndeterminate() const
+{
+    return element().indeterminate();
 }
 
 } // namespace WebCore
index 3d38e1f..ec0b70e 100644 (file)
@@ -47,7 +47,8 @@ private:
     void willDispatchClick(InputElementClickState&) override;
     void didDispatchClick(Event*, const InputElementClickState&) override;
     bool isCheckbox() const override;
-    bool supportsIndeterminateAppearance() const override;
+    bool matchesIndeterminatePseudoClass() const override;
+    bool shouldAppearIndeterminate() const override;
 };
 
 } // namespace WebCore
index c86ef4a..7abb34b 100644 (file)
@@ -861,7 +861,7 @@ void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventB
     setNeedsStyleRecalc();
 
     if (RadioButtonGroups* buttons = radioButtonGroups())
-            buttons->updateCheckedState(this);
+        buttons->updateCheckedState(this);
     if (renderer() && renderer()->style().hasAppearance())
         renderer()->theme().stateChanged(*renderer(), ControlStates::CheckedState);
     updateValidity();
@@ -1763,9 +1763,21 @@ String HTMLInputElement::defaultToolTip() const
     return m_inputType->defaultToolTip();
 }
 
+bool HTMLInputElement::matchesIndeterminatePseudoClass() const
+{
+    // For input elements, matchesIndeterminatePseudoClass()
+    // is not equivalent to shouldAppearIndeterminate() because of radio button.
+    //
+    // A group of radio button without any checked button is indeterminate
+    // for the :indeterminate selector. On the other hand, RenderTheme
+    // currently only supports single element being indeterminate.
+    // Because of this, radio is indetermindate for CSS but not for render theme.
+    return m_inputType->matchesIndeterminatePseudoClass();
+}
+
 bool HTMLInputElement::shouldAppearIndeterminate() const 
 {
-    return m_inputType->supportsIndeterminateAppearance() && indeterminate();
+    return m_inputType->shouldAppearIndeterminate();
 }
 
 #if ENABLE(MEDIA_CAPTURE)
@@ -1801,12 +1813,12 @@ HTMLInputElement* HTMLInputElement::checkedRadioButtonForGroup() const
 RadioButtonGroups* HTMLInputElement::radioButtonGroups() const
 {
     if (!isRadioButton())
-        return 0;
+        return nullptr;
     if (HTMLFormElement* formElement = form())
         return &formElement->radioButtonGroups();
     if (inDocument())
         return &document().formController().radioButtonGroups();
-    return 0;
+    return nullptr;
 }
 
 inline void HTMLInputElement::addToRadioButtonGroup()
index ed7c50f..f79d80a 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2010, 2016 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Samsung Electronics. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -162,6 +162,7 @@ public:
     void setIndeterminate(bool);
     // shouldAppearChecked is used by the rendering tree/CSS while checked() is used by JS to determine checked state
     bool shouldAppearChecked() const;
+    bool matchesIndeterminatePseudoClass() const final;
     bool shouldAppearIndeterminate() const final;
 
     unsigned size() const;
@@ -274,6 +275,8 @@ public:
     Vector<HTMLInputElement*> radioButtonGroup() const;
     HTMLInputElement* checkedRadioButtonForGroup() const;
     bool isInRequiredRadioButtonGroup();
+    // Returns null if this isn't associated with any radio button group.
+    RadioButtonGroups* radioButtonGroups() const;
 
     // Functions for InputType classes.
     void setValueInternal(const String&, TextFieldEventBehavior);
@@ -422,8 +425,6 @@ private:
     void maxLengthAttributeChanged(const AtomicString& newValue);
     void updateValueIfNeeded();
 
-    // Returns null if this isn't associated with any radio button group.
-    RadioButtonGroups* radioButtonGroups() const;
     void addToRadioButtonGroup();
     void removeFromRadioButtonGroup();
 
index 5836321..3aaf414 100644 (file)
@@ -966,7 +966,12 @@ Optional<Decimal> InputType::findClosestTickMarkValue(const Decimal&)
 }
 #endif
 
-bool InputType::supportsIndeterminateAppearance() const
+bool InputType::matchesIndeterminatePseudoClass() const
+{
+    return false;
+}
+
+bool InputType::shouldAppearIndeterminate() const
 {
     return false;
 }
index 4bcb843..60b3a4c 100644 (file)
@@ -267,7 +267,8 @@ public:
     virtual void capsLockStateMayHaveChanged();
     virtual void updateAutoFillButton();
     virtual String defaultToolTip() const;
-    virtual bool supportsIndeterminateAppearance() const;
+    virtual bool matchesIndeterminatePseudoClass() const;
+    virtual bool shouldAppearIndeterminate() const;
     virtual bool supportsSelectionAPI() const;
     virtual Color valueAsColor() const;
     virtual void selectColor(const Color&);
index 67877ce..460fce2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2011, 2016 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -151,13 +151,6 @@ void RadioInputType::willDispatchClick(InputElementClickState& state)
     state.checked = element().checked();
     state.checkedRadioButton = element().checkedRadioButtonForGroup();
 
-#if PLATFORM(IOS)
-    state.indeterminate = element().indeterminate();
-
-    if (element().indeterminate())
-        element().setIndeterminate(false);
-#endif
-
     element().setChecked(true, DispatchChangeEvent);
 }
 
@@ -173,11 +166,6 @@ void RadioInputType::didDispatchClick(Event* event, const InputElementClickState
                 && checkedRadioButton->name() == element().name()) {
             checkedRadioButton->setChecked(true);
         }
-
-#if PLATFORM(IOS)        
-        element().setIndeterminate(state.indeterminate);
-#endif
-
     }
 
     // The work we did in willDispatchClick was default handling.
@@ -189,13 +177,12 @@ bool RadioInputType::isRadioButton() const
     return true;
 }
 
-bool RadioInputType::supportsIndeterminateAppearance() const
+bool RadioInputType::matchesIndeterminatePseudoClass() const
 {
-#if PLATFORM(IOS)
-    return true;
-#else
-    return false;
-#endif
+    const HTMLInputElement& element = this->element();
+    if (const RadioButtonGroups* radioButtonGroups = element.radioButtonGroups())
+        return !radioButtonGroups->hasCheckedButton(&element);
+    return !element.checked();
 }
 
 } // namespace WebCore
index be3f7c1..1425e7b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -51,7 +52,7 @@ private:
     void willDispatchClick(InputElementClickState&) override;
     void didDispatchClick(Event*, const InputElementClickState&) override;
     bool isRadioButton() const override;
-    bool supportsIndeterminateAppearance() const override;
+    bool matchesIndeterminatePseudoClass() const override;
 };
 
 } // namespace WebCore
index 216103b..bbd2c3e 100644 (file)
@@ -179,8 +179,6 @@ static bool canShareStyleWithControl(const HTMLFormControlElement& element, cons
         return false;
     if (thisInputElement.shouldAppearChecked() != otherInputElement.shouldAppearChecked())
         return false;
-    if (thisInputElement.shouldAppearIndeterminate() != otherInputElement.shouldAppearIndeterminate())
-        return false;
     if (thisInputElement.isRequired() != otherInputElement.isRequired())
         return false;
 
@@ -283,6 +281,9 @@ bool SharingResolver::canShareStyleWithElement(const Context& context, const Sty
     if (element.matchesInvalidPseudoClass() != element.matchesValidPseudoClass())
         return false;
 
+    if (candidateElement.matchesIndeterminatePseudoClass() != element.matchesIndeterminatePseudoClass())
+        return false;
+
     if (element.shadowRoot() && !element.shadowRoot()->styleResolver().ruleSets().authorStyle()->hostPseudoClassRules().isEmpty())
         return false;
 
@@ -329,11 +330,6 @@ bool SharingResolver::sharingCandidateHasIdenticalStyleAffectingAttributes(const
     if (const_cast<StyledElement&>(element).presentationAttributeStyle() != const_cast<StyledElement&>(sharingCandidate).presentationAttributeStyle())
         return false;
 
-    if (element.hasTagName(HTMLNames::progressTag)) {
-        if (element.shouldAppearIndeterminate() != sharingCandidate.shouldAppearIndeterminate())
-            return false;
-    }
-
     return true;
 }