Diff
Modified: trunk/LayoutTests/ChangeLog (202244 => 202245)
--- trunk/LayoutTests/ChangeLog 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/LayoutTests/ChangeLog 2016-06-20 21:30:36 UTC (rev 202245)
@@ -1,3 +1,15 @@
+2016-06-20 Benjamin Poulain <benja...@webkit.org>
+
+ :default CSS pseudo-class should match checkboxes+radios with a `checked` attribute
+ https://bugs.webkit.org/show_bug.cgi?id=156230
+
+ Reviewed by Alex Christensen.
+
+ * fast/css/pseudo-default-basics-expected.html: Added.
+ * fast/css/pseudo-default-basics.html: Added.
+ * fast/selectors/default-style-update-expected.txt: Added.
+ * fast/selectors/default-style-update.html: Added.
+
2016-06-20 Simon Fraser <simon.fra...@apple.com>
Focus event dispatched in iframe causes parent document to scroll incorrectly
Added: trunk/LayoutTests/fast/css/pseudo-default-basics-expected.html (0 => 202245)
--- trunk/LayoutTests/fast/css/pseudo-default-basics-expected.html (rev 0)
+++ trunk/LayoutTests/fast/css/pseudo-default-basics-expected.html 2016-06-20 21:30:36 UTC (rev 202245)
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+ * {
+ -webkit-appearance: none;
+ }
+ form, fieldset, option {
+ display: inline;
+ }
+ .default, .default + span {
+ background-color: green;
+ padding: 2px;
+ }
+ input:matches([type=checkbox], [type=radio] {
+ width: 30px;
+ height: 30px;
+ }
+</style>
+</head>
+<body>
+ <div>
+ <!-- Default button of type button -->
+ <button>Out of form</button>
+ <form>
+ <button class="default">First</button>
+ </form>
+ <form>
+ <button class="default">First</button>
+ <button>Second</button>
+ </form>
+ <form>
+ <fieldset>
+ <button class="default">First</button>
+ </fieldset>
+ <button>Second</button>
+ </form>
+ </div>
+ <div>
+ <!-- Default button of type input-->
+ <input>
+ <input type="text">
+ <input type="submit">
+ <input type="image">
+ <input value="WebKit">
+ <input type="text" value="WebKit">
+ <input type="submit" value="WebKit">
+ <input type="image" value="WebKit">
+ <form>
+ <input>
+ </form>
+ <form>
+ <input type="submit" class="default">
+ </form>
+ <form>
+ <input type="image" class="default">
+ </form>
+ <form>
+ <input>
+ <input type="submit" class="default">
+ <input type="image">
+ </form>
+ <form>
+ <input type="text">
+ <input type="image" class="default">
+ <input type="submit">
+ </form>
+ <form>
+ <button class="default">Button</button>
+ <input type="image">
+ <input type="submit">
+ </form>
+ <form>
+ <input type="submit" class="default">
+ <button>Button</button>
+ <input type="image">
+ </form>
+ <form>
+ <input type="image" class="default">
+ <input type="submit">
+ <button>Button</button>
+ </form>
+ </div>
+ <div>
+ <!-- Check boxes and radio button -->
+ <input type="checkbox"><span>A</span>
+ <input type="radio"><span>A</span>
+ <input type="checkbox" name="group1"><span>A</span>
+ <input type="radio" name="group1"><span>A</span>
+ <input type="checkbox" checked class="default"><span>A</span>
+ <input type="radio" checked class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked class="default"><span>A</span>
+ <input type="radio" name="group1" checked class="default"><span>A</span>
+ <input type="checkbox" checked=false class="default"><span>A</span>
+ <input type="radio" checked=false class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked=false class="default"><span>A</span>
+ <input type="radio" name="group1" checked=false class="default"><span>A</span>
+ <input type="checkbox" checked=webkit class="default"><span>A</span>
+ <input type="radio" checked=webkit class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked=webkit class="default"><span>A</span>
+ <input type="radio" name="group1" checked=webkit class="default"><span>A</span>
+ <input type="checkbox" disabled><span>A</span>
+ <input type="radio" disabled><span>A</span>
+ <input type="checkbox" name="group1" disabled><span>A</span>
+ <input type="radio" name="group1" disabled><span>A</span>
+ <input type="checkbox" checked disabled class="default"><span>A</span>
+ <input type="radio" checked disabled class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked disabled class="default"><span>A</span>
+ <input type="radio" name="group1" checked disabled class="default"><span>A</span>
+ <form>
+ <input type="checkbox"><span>A</span>
+ <input type="radio"><span>A</span>
+ <input type="checkbox" name="group1"><span>A</span>
+ <input type="radio" name="group1"><span>A</span>
+ <input type="checkbox" checked class="default"><span>A</span>
+ <input type="radio" checked class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked class="default"><span>A</span>
+ <input type="radio" name="group1" checked class="default"><span>A</span>
+ <input type="checkbox" checked=false class="default"><span>A</span>
+ <input type="radio" checked=false class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked=false class="default"><span>A</span>
+ <input type="radio" name="group1" checked=false class="default"><span>A</span>
+ <input type="checkbox" checked=webkit class="default"><span>A</span>
+ <input type="radio" checked=webkit class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked=webkit class="default"><span>A</span>
+ <input type="radio" name="group1" checked=webkit class="default"><span>A</span>
+ <input type="checkbox" disabled><span>A</span>
+ <input type="radio" disabled><span>A</span>
+ <input type="checkbox" name="group1" disabled><span>A</span>
+ <input type="radio" name="group1" disabled><span>A</span>
+ <input type="checkbox" checked disabled class="default"><span>A</span>
+ <input type="radio" checked disabled class="default"><span>A</span>
+ <input type="checkbox" name="group1" checked disabled class="default"><span>A</span>
+ <input type="radio" name="group1" checked disabled class="default"><span>A</span>
+ </form>
+ </div>
+ <div>
+ <option>Option</option><span>Option</span>
+ <option selected class="default">Option</option><span>Option</span>
+ <option selected=false class="default">Option</option><span>Option</span>
+ <option selected=WebKit class="default">Option</option><span>Option</span>
+ <select>
+ <option>Option</option><span>Option</span>
+ <option selected class="default">Option</option><span>Option</span>
+ <option selected=false class="default">Option</option><span>Option</span>
+ <option selected=WebKit class="default">Option</option><span>Option</span>
+ </select>
+ </div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/pseudo-default-basics.html (0 => 202245)
--- trunk/LayoutTests/fast/css/pseudo-default-basics.html (rev 0)
+++ trunk/LayoutTests/fast/css/pseudo-default-basics.html 2016-06-20 21:30:36 UTC (rev 202245)
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+ * {
+ -webkit-appearance: none;
+ }
+ form, fieldset, option {
+ display: inline;
+ }
+ :default, :default + span {
+ background-color: green;
+ padding: 2px;
+ }
+ input:matches([type=checkbox], [type=radio] {
+ width: 30px;
+ height: 30px;
+ }
+</style>
+</head>
+<body>
+ <div>
+ <!-- Default button of type button -->
+ <button>Out of form</button>
+ <form>
+ <button>First</button>
+ </form>
+ <form>
+ <button>First</button>
+ <button>Second</button>
+ </form>
+ <form>
+ <fieldset>
+ <button>First</button>
+ </fieldset>
+ <button>Second</button>
+ </form>
+ </div>
+ <div>
+ <!-- Default button of type input-->
+ <input>
+ <input type="text">
+ <input type="submit">
+ <input type="image">
+ <input value="WebKit">
+ <input type="text" value="WebKit">
+ <input type="submit" value="WebKit">
+ <input type="image" value="WebKit">
+ <form>
+ <input>
+ </form>
+ <form>
+ <input type="submit">
+ </form>
+ <form>
+ <input type="image">
+ </form>
+ <form>
+ <input>
+ <input type="submit">
+ <input type="image">
+ </form>
+ <form>
+ <input type="text">
+ <input type="image">
+ <input type="submit">
+ </form>
+ <form>
+ <button>Button</button>
+ <input type="image">
+ <input type="submit">
+ </form>
+ <form>
+ <input type="submit">
+ <button>Button</button>
+ <input type="image">
+ </form>
+ <form>
+ <input type="image">
+ <input type="submit">
+ <button>Button</button>
+ </form>
+ </div>
+ <div>
+ <!-- Check boxes and radio button -->
+ <input type="checkbox"><span>A</span>
+ <input type="radio"><span>A</span>
+ <input type="checkbox" name="group1"><span>A</span>
+ <input type="radio" name="group1"><span>A</span>
+ <input type="checkbox" checked><span>A</span>
+ <input type="radio" checked><span>A</span>
+ <input type="checkbox" name="group1" checked><span>A</span>
+ <input type="radio" name="group1" checked><span>A</span>
+ <input type="checkbox" checked=false><span>A</span>
+ <input type="radio" checked=false><span>A</span>
+ <input type="checkbox" name="group1" checked=false><span>A</span>
+ <input type="radio" name="group1" checked=false><span>A</span>
+ <input type="checkbox" checked=webkit><span>A</span>
+ <input type="radio" checked=webkit><span>A</span>
+ <input type="checkbox" name="group1" checked=webkit><span>A</span>
+ <input type="radio" name="group1" checked=webkit><span>A</span>
+ <input type="checkbox" disabled><span>A</span>
+ <input type="radio" disabled><span>A</span>
+ <input type="checkbox" name="group1" disabled><span>A</span>
+ <input type="radio" name="group1" disabled><span>A</span>
+ <input type="checkbox" checked disabled><span>A</span>
+ <input type="radio" checked disabled><span>A</span>
+ <input type="checkbox" name="group1" checked disabled><span>A</span>
+ <input type="radio" name="group1" checked disabled><span>A</span>
+ <form>
+ <input type="checkbox"><span>A</span>
+ <input type="radio"><span>A</span>
+ <input type="checkbox" name="group1"><span>A</span>
+ <input type="radio" name="group1"><span>A</span>
+ <input type="checkbox" checked><span>A</span>
+ <input type="radio" checked><span>A</span>
+ <input type="checkbox" name="group1" checked><span>A</span>
+ <input type="radio" name="group1" checked><span>A</span>
+ <input type="checkbox" checked=false><span>A</span>
+ <input type="radio" checked=false><span>A</span>
+ <input type="checkbox" name="group1" checked=false><span>A</span>
+ <input type="radio" name="group1" checked=false><span>A</span>
+ <input type="checkbox" checked=webkit><span>A</span>
+ <input type="radio" checked=webkit><span>A</span>
+ <input type="checkbox" name="group1" checked=webkit><span>A</span>
+ <input type="radio" name="group1" checked=webkit><span>A</span>
+ <input type="checkbox" disabled><span>A</span>
+ <input type="radio" disabled><span>A</span>
+ <input type="checkbox" name="group1" disabled><span>A</span>
+ <input type="radio" name="group1" disabled><span>A</span>
+ <input type="checkbox" checked disabled><span>A</span>
+ <input type="radio" checked disabled><span>A</span>
+ <input type="checkbox" name="group1" checked disabled><span>A</span>
+ <input type="radio" name="group1" checked disabled><span>A</span>
+ </form>
+ </div>
+ <div>
+ <option>Option</option><span>Option</span>
+ <option selected>Option</option><span>Option</span>
+ <option selected=false>Option</option><span>Option</span>
+ <option selected=WebKit>Option</option><span>Option</span>
+ <select>
+ <option>Option</option><span>Option</span>
+ <option selected>Option</option><span>Option</span>
+ <option selected=false>Option</option><span>Option</span>
+ <option selected=WebKit>Option</option><span>Option</span>
+ </select>
+ </div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/selectors/default-style-update-expected.txt (0 => 202245)
--- trunk/LayoutTests/fast/selectors/default-style-update-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/selectors/default-style-update-expected.txt 2016-06-20 21:30:36 UTC (rev 202245)
@@ -0,0 +1,148 @@
+Check the basic features of the :default pseudo class
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initial State
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Changing input1 input a submit button, it should become the form's default button
+PASS elementsStyledWithDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Changing button order in the first form
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Removing button2 from its form
+PASS elementsStyledWithDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS button2.matches(":default") is false
+Adding back button2
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Setting button2 type to button
+PASS elementsStyledWithDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Setting button2 type to submit
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Setting button2 type to reset
+PASS elementsStyledWithDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Setting button2 type to webkit
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Removing the type attribute from button2
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Removing input1, button2 is still the first button
+PASS elementsStyledWithDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS input1.matches(":default") is false
+Removing the form from the tree
+PASS elementsStyledWithDefaultSelector() is ["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS button2.matches(":default") is true
+PASS input1.matches(":default") is false
+Changing the type of input2 to password
+PASS elementsStyledWithDefaultSelector() is ["button3", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Changing the type of input2 to image
+PASS elementsStyledWithDefaultSelector() is ["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Moving input2 to the form of input3, before input3
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Moving button5 inside the first form, but button5 has a form attribute
+PASS elementsStyledWithDefaultSelector() is ["button5", "button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button5", "button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]
+Removing button 5
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS button5.matches(":default") is false
+Moving input5 above button 6
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]
+Changing input5's type to submit
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]
+Changing input5's type to reset
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]
+Removing free-form-1
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]
+Changing input5's type to image
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input5", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input5", "input7", "input9", "input11", "option2", "option4"]
+Removing input5
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]
+PASS input5.matches(":default") is false
+Removing button6
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input7", "input9", "input11", "option2", "option4"]
+PASS button6.matches(":default") is false
+Setting input6 as checked
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input7", "input9", "input11", "option2", "option4"]
+Setting the checked attribute to input6
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]
+Setting input7 as unchecked
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]
+Setting input7's checked attribute to 'WebKit'
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]
+Removing input7's checked attribute
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input6", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input6", "input9", "input11", "option2", "option4"]
+Setting input7's selected attribute
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input6", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input6", "input9", "input11", "option2", "option4"]
+Removing input6
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input9", "input11", "option2", "option4"]
+PASS input6.matches(":default") is true
+Setting input8 as checked
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input9", "input11", "option2", "option4"]
+Setting the checked attribute to input8
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "input9", "input11", "option2", "option4"]
+Setting input9 as unchecked
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "input9", "input11", "option2", "option4"]
+Setting input9's checked attribute to 'WebKit'
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "input9", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "input9", "input11", "option2", "option4"]
+Removing input9's checked attribute
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "input11", "option2", "option4"]
+Setting input9's selected attribute
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "input11", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "input11", "option2", "option4"]
+Removing input11
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "option2", "option4"]
+PASS input11.matches(":default") is true
+Add the selected attribute to option1
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "option1", "option2", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "option1", "option2", "option4"]
+Remove the selected attribute from option2
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "option1", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "option1", "option4"]
+Set the checked attribute on option2
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "option1", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "option1", "option4"]
+Add the selected attribute to false on option3
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "option1", "option3", "option4"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "option1", "option3", "option4"]
+Remove option4
+PASS elementsStyledWithDefaultSelector() is ["button3", "input2", "input8", "option1", "option3"]
+PASS elementsMatchingDefaultSelector() is ["button3", "input2", "input8", "option1", "option3"]
+PASS option4.matches(":default") is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/selectors/default-style-update.html (0 => 202245)
--- trunk/LayoutTests/fast/selectors/default-style-update.html (rev 0)
+++ trunk/LayoutTests/fast/selectors/default-style-update.html 2016-06-20 21:30:36 UTC (rev 202245)
@@ -0,0 +1,337 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<style>
+#webkit-test * {
+ background-color: white;
+}
+#webkit-test :default {
+ background-color: rgb(1, 2, 3);
+}
+</style>
+</head>
+<body>
+ <div style="display:none" id="webkit-test">
+ <button id="button1">Button1</button>
+ <form>
+ <input id="input1">
+ <button id="button2">Button2</button>
+ </form>
+ <form>
+ <input id="input2" type="submit">
+ <button id="button3">Button3</button>
+ </form>
+ <form>
+ <input id="input3" type="image">
+ <button id="button4">Button4</button>
+ </form>
+ <form id="free-form-1">
+ </form>
+ <input id="input4" type="submit" form="free-form-1">
+ <button id="button5" form="free-form-1">Button5</button>
+ <form id="free-form-2">
+ </form>
+ <button id="button6" form="free-form-2">Button6</button>
+ <input id="input5" type="image" form="free-form-2">
+ <input type="checkbox" id="input6">
+ <input type="checkbox" id="input7" checked>
+ <input type="radio" id="input8">
+ <input type="radio" id="input9" checked>
+ <input type="radio" id="input10" name="group1">
+ <input type="radio" id="input11" name="group1" checked>
+ <option id="option1">Option</option>
+ <option selected id="option2">Option</option>
+ <select>
+ <option id="option3">Option</option>
+ <option selected id="option4">Option</option>
+ </select>
+ </div>
+</body>
+<script>
+"use strict";
+description('Check the basic features of the :default pseudo class');
+
+function elementsStyledWithDefaultSelector() {
+ let elements = [];
+ for (let element of document.querySelectorAll("#webkit-test *")) {
+ if (getComputedStyle(element).backgroundColor === 'rgb(1, 2, 3)') {
+ elements.push(element.id);
+ }
+ }
+ return elements;
+}
+
+function elementsMatchingDefaultSelector() {
+ let elements = [];
+ for (let element of document.querySelectorAll(":default")) {
+ elements.push(element.id);
+ }
+ return elements;
+}
+
+debug("Initial State");
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Changing input1 input a submit button, it should become the form's default button");
+var input1 = document.getElementById("input1");
+input1.type = "submit";
+shouldBe('elementsStyledWithDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Changing button order in the first form");
+input1.parentElement.appendChild(input1);
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing button2 from its form");
+var button2 = document.getElementById("button2");
+button2.parentElement.removeChild(button2);
+shouldBe('elementsStyledWithDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBeFalse('button2.matches(":default")');
+
+debug("Adding back button2");
+input1.parentElement.insertBefore(button2, input1);
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting button2 type to button");
+button2.setAttribute("type", "button");
+shouldBe('elementsStyledWithDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting button2 type to submit");
+button2.setAttribute("type", "submit");
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting button2 type to reset");
+button2.setAttribute("type", "reset");
+shouldBe('elementsStyledWithDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["input1", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting button2 type to webkit");
+button2.setAttribute("type", "webkit");
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing the type attribute from button2");
+button2.removeAttribute("type");
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing input1, button2 is still the first button");
+input1.parentElement.removeChild(input1);
+shouldBe('elementsStyledWithDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button2", "input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBeFalse('input1.matches(":default")');
+
+debug("Removing the form from the tree");
+var firstRemovedForm = button2.parentElement;
+firstRemovedForm.parentElement.removeChild(firstRemovedForm);
+shouldBe('elementsStyledWithDefaultSelector()', '["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBeTrue('button2.matches(":default")');
+shouldBeFalse('input1.matches(":default")');
+
+// Give GC a chance to collect.
+firstRemovedForm = undefined;
+input1 = undefined;
+button2 = undefined;
+
+debug("Changing the type of input2 to password");
+var input2 = document.getElementById("input2");
+input2.setAttribute("type", "password");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Changing the type of input2 to image");
+input2.setAttribute("type", "image");
+shouldBe('elementsStyledWithDefaultSelector()', '["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["input2", "input3", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Moving input2 to the form of input3, before input3");
+var input3 = document.getElementById("input3");
+input3.parentElement.insertBefore(input2, input3);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Moving button5 inside the first form, but button5 has a form attribute");
+var button3 = document.getElementById("button3");
+var button5 = document.getElementById("button5");
+button3.parentElement.insertBefore(button5, button3);
+shouldBe('elementsStyledWithDefaultSelector()', '["button5", "button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button5", "button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing button 5");
+button5.parentElement.removeChild(button5);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBeFalse('button5.matches(":default")');
+button5 = undefined;
+
+debug("Moving input5 above button 6");
+var input5 = document.getElementById("input5");
+var button6 = document.getElementById("button6");
+input5.parentElement.insertBefore(input5, button6)
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Changing input5's type to submit");
+input5.type = "SUBMIT";
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input4", "input5", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Changing input5's type to reset");
+input5.type = "reset";
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input4", "button6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing free-form-1");
+var freeForm1 = document.getElementById("free-form-1");
+freeForm1.parentElement.removeChild(freeForm1);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]');
+freeForm1 = undefined;
+
+debug("Changing input5's type to image");
+input5.type = "image";
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input5", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input5", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing input5");
+input5.parentElement.removeChild(input5);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "button6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBeFalse('input5.matches(":default")');
+input5 = undefined;
+
+debug("Removing button6");
+button6.parentElement.removeChild(button6);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input7", "input9", "input11", "option2", "option4"]');
+shouldBeFalse('button6.matches(":default")');
+button6 = undefined;
+
+debug("Setting input6 as checked");
+var input6 = document.getElementById("input6");
+// Interestingly, the selector looks for the attribute, not the state.
+input6.checked = true;
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting the checked attribute to input6");
+input6.setAttribute("checked", "");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting input7 as unchecked");
+var input7 = document.getElementById("input7");
+// Interestingly, the selector looks for the attribute, not the state.
+input7.checked = false;
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Setting input7's checked attribute to 'WebKit'");
+input7.setAttribute("checked", "WebKit");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input6", "input7", "input9", "input11", "option2", "option4"]');
+
+debug("Removing input7's checked attribute");
+input7.removeAttribute("checked");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input6", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input6", "input9", "input11", "option2", "option4"]');
+
+debug("Setting input7's selected attribute");
+input7.setAttribute("selected", "");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input6", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input6", "input9", "input11", "option2", "option4"]');
+
+debug("Removing input6");
+input6.parentElement.removeChild(input6);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input9", "input11", "option2", "option4"]');
+shouldBeTrue('input6.matches(":default")');
+input6 = undefined;
+
+debug("Setting input8 as checked");
+var input8 = document.getElementById("input8");
+// Interestingly, the selector looks for the attribute, not the state.
+input8.checked = true;
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input9", "input11", "option2", "option4"]');
+
+debug("Setting the checked attribute to input8");
+input8.setAttribute("checked", "");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "input9", "input11", "option2", "option4"]');
+
+debug("Setting input9 as unchecked");
+var input9 = document.getElementById("input9");
+// Interestingly, the selector looks for the attribute, not the state.
+input9.checked = false;
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "input9", "input11", "option2", "option4"]');
+
+debug("Setting input9's checked attribute to 'WebKit'");
+input9.setAttribute("checked", "WebKit");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "input9", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "input9", "input11", "option2", "option4"]');
+
+debug("Removing input9's checked attribute");
+input9.removeAttribute("checked");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "input11", "option2", "option4"]');
+
+debug("Setting input9's selected attribute");
+input9.setAttribute("selected", "");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "input11", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "input11", "option2", "option4"]');
+
+debug("Removing input11");
+var input11 = document.getElementById("input11");
+input11.parentElement.removeChild(input11);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "option2", "option4"]');
+shouldBeTrue('input11.matches(":default")');
+input11 = undefined;
+
+debug("Add the selected attribute to option1");
+var option1 = document.getElementById("option1");
+option1.setAttribute("selected", "");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "option1", "option2", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "option1", "option2", "option4"]');
+
+debug("Remove the selected attribute from option2");
+var option2 = document.getElementById("option2");
+option2.removeAttribute("selected");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "option1", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "option1", "option4"]');
+
+debug("Set the checked attribute on option2");
+var option2 = document.getElementById("option2");
+option2.setAttribute("checked", "");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "option1", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "option1", "option4"]');
+
+debug("Add the selected attribute to false on option3");
+var option3 = document.getElementById("option3");
+option3.setAttribute("selected", "false");
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "option1", "option3", "option4"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "option1", "option3", "option4"]');
+
+debug("Remove option4");
+var option4 = document.getElementById("option4");
+option4.parentElement.removeChild(option4);
+shouldBe('elementsStyledWithDefaultSelector()', '["button3", "input2", "input8", "option1", "option3"]');
+shouldBe('elementsMatchingDefaultSelector()', '["button3", "input2", "input8", "option1", "option3"]');
+shouldBeTrue('option4.matches(":default")');
+option4 = undefined;
+
+gc();
+
+</script>
+<script src=""
+</html>
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (202244 => 202245)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2016-06-20 21:30:36 UTC (rev 202245)
@@ -1,3 +1,12 @@
+2016-06-20 Benjamin Poulain <benja...@webkit.org>
+
+ :default CSS pseudo-class should match checkboxes+radios with a `checked` attribute
+ https://bugs.webkit.org/show_bug.cgi?id=156230
+
+ Reviewed by Alex Christensen.
+
+ * web-platform-tests/html/semantics/selectors/pseudo-classes/default-expected.txt:
+
2016-06-17 Benjamin Poulain <benja...@webkit.org>
:indeterminate pseudo-class should match radios whose group has no checked radio
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/default-expected.txt (202244 => 202245)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/default-expected.txt 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/default-expected.txt 2016-06-20 21:30:36 UTC (rev 202245)
@@ -10,6 +10,6 @@
button6 button7
button8 button9
-FAIL ':default' matches <button>s that are their form's default button, <input>s of type submit/image that are their form's default button, checked <input>s and selected <option>s assert_array_equals: lengths differ, expected 10 got 7
-FAIL ':default' matches dynamically changed form's default buttons assert_array_equals: lengths differ, expected 10 got 7
+PASS ':default' matches <button>s that are their form's default button, <input>s of type submit/image that are their form's default button, checked <input>s and selected <option>s
+PASS ':default' matches dynamically changed form's default buttons
Modified: trunk/Source/WebCore/ChangeLog (202244 => 202245)
--- trunk/Source/WebCore/ChangeLog 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/ChangeLog 2016-06-20 21:30:36 UTC (rev 202245)
@@ -1,3 +1,82 @@
+2016-06-20 Benjamin Poulain <benja...@webkit.org>
+
+ :default CSS pseudo-class should match checkboxes+radios with a `checked` attribute
+ https://bugs.webkit.org/show_bug.cgi?id=156230
+
+ Reviewed by Alex Christensen.
+
+ This patch update the :default pseudo class matching to be closer to the spec:
+ https://html.spec.whatwg.org/multipage/scripting.html#selector-default
+
+ The main remaining difference with the spec is the definition of "default button".
+ This is an unrelated problem that should be addressed separately.
+
+ The implementation was missing support for:
+ -input elements of type "checkbox" or "radio" with the "checked" attribute defined.
+ -option elements with the "selected" attribute defined.
+
+ The existing support for default button was pretty bad, I fixed that too.
+ The owner form now has a resetDefaultButton() API. When a Form Associated Element
+ becomes a submit button or loses that property, the element calls its form
+ to update the style as needed.
+
+ Whenever the submit button changes, 2 elements needs to have their style invalidated:
+ -The former default button.
+ -The new default button.
+ To invalidate the former button, FormElement now caches the computed
+ default button. When the default button changes, the cached value is invalidated
+ in addition to the new value.
+
+ Computing the new default button takes linear time in the number of form associated element.
+ To mitigate that, resetDefaultButton() is only called when changes are related
+ to submit buttons. Since those changes are rare, I don't expect the invalidation
+ to be a problem.
+
+ Tests: fast/css/pseudo-default-basics.html
+ fast/selectors/default-style-update.html
+
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::checkOne):
+ * css/SelectorCheckerTestFunctions.h:
+ (WebCore::matchesDefaultPseudoClass):
+ (WebCore::isDefaultButtonForForm): Deleted.
+ * cssjit/SelectorCompiler.cpp:
+ (WebCore::SelectorCompiler::addPseudoClassType):
+ * dom/Element.cpp:
+ (WebCore::Element::matchesValidPseudoClass):
+ (WebCore::Element::matchesInvalidPseudoClass):
+ (WebCore::Element::matchesDefaultPseudoClass):
+ * dom/Element.h:
+ (WebCore::Element::matchesValidPseudoClass): Deleted.
+ (WebCore::Element::matchesInvalidPseudoClass): Deleted.
+ (WebCore::Element::isDefaultButtonForForm): Deleted.
+ * html/HTMLButtonElement.cpp:
+ (WebCore::HTMLButtonElement::parseAttribute):
+ (WebCore::HTMLButtonElement::matchesDefaultPseudoClass):
+ * html/HTMLButtonElement.h:
+ * html/HTMLFormControlElement.cpp:
+ (WebCore::HTMLFormControlElement::isDefaultButtonForForm): Deleted.
+ * html/HTMLFormControlElement.h:
+ * html/HTMLFormElement.cpp:
+ (WebCore::HTMLFormElement::~HTMLFormElement):
+ (WebCore::HTMLFormElement::registerFormElement):
+ (WebCore::HTMLFormElement::removeFormElement):
+ (WebCore::HTMLFormElement::defaultButton):
+ (WebCore::HTMLFormElement::resetDefaultButton):
+ * html/HTMLFormElement.h:
+ * html/HTMLInputElement.cpp:
+ (WebCore::HTMLInputElement::updateType):
+ (WebCore::HTMLInputElement::parseAttribute):
+ (WebCore::HTMLInputElement::matchesDefaultPseudoClass):
+ * html/HTMLInputElement.h:
+ * html/HTMLOptionElement.cpp:
+ (WebCore::HTMLOptionElement::matchesDefaultPseudoClass):
+ (WebCore::HTMLOptionElement::parseAttribute):
+ * html/HTMLOptionElement.h:
+ * style/StyleSharingResolver.cpp:
+ (WebCore::Style::SharingResolver::canShareStyleWithElement):
+ (WebCore::Style::canShareStyleWithControl): Deleted.
+
2016-06-20 Simon Fraser <simon.fra...@apple.com>
Focus event dispatched in iframe causes parent document to scroll incorrectly
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (202244 => 202245)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -965,7 +965,7 @@
case CSSSelector::PseudoClassFullPageMedia:
return isMediaDocument(element);
case CSSSelector::PseudoClassDefault:
- return isDefaultButtonForForm(element);
+ return matchesDefaultPseudoClass(element);
case CSSSelector::PseudoClassDisabled:
return isDisabled(element);
case CSSSelector::PseudoClassReadOnly:
Modified: trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h (202244 => 202245)
--- trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -46,9 +46,9 @@
return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element).isAutoFilled();
}
-ALWAYS_INLINE bool isDefaultButtonForForm(const Element& element)
+ALWAYS_INLINE bool matchesDefaultPseudoClass(const Element& element)
{
- return element.isDefaultButtonForForm();
+ return element.matchesDefaultPseudoClass();
}
ALWAYS_INLINE bool isDisabled(const Element& element)
Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (202244 => 202245)
--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -535,7 +535,7 @@
fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(isChecked));
return FunctionType::SimpleSelectorChecker;
case CSSSelector::PseudoClassDefault:
- fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(isDefaultButtonForForm));
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(matchesDefaultPseudoClass));
return FunctionType::SimpleSelectorChecker;
case CSSSelector::PseudoClassDisabled:
fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr(isDisabled));
Modified: trunk/Source/WebCore/dom/Element.cpp (202244 => 202245)
--- trunk/Source/WebCore/dom/Element.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/dom/Element.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -2747,6 +2747,16 @@
elementRareData()->setAfterPseudoElement(nullptr);
}
+bool Element::matchesValidPseudoClass() const
+{
+ return false;
+}
+
+bool Element::matchesInvalidPseudoClass() const
+{
+ return false;
+}
+
bool Element::matchesReadWritePseudoClass() const
{
return false;
@@ -2757,6 +2767,11 @@
return shouldAppearIndeterminate();
}
+bool Element::matchesDefaultPseudoClass() const
+{
+ return false;
+}
+
bool Element::matches(const String& selector, ExceptionCode& ec)
{
SelectorQuery* selectorQuery = document().selectorQueryForString(selector, ec);
Modified: trunk/Source/WebCore/dom/Element.h (202244 => 202245)
--- trunk/Source/WebCore/dom/Element.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/dom/Element.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -391,8 +391,11 @@
bool childNeedsShadowWalker() const;
void didShadowTreeAwareChildrenChange();
+ virtual bool matchesValidPseudoClass() const;
+ virtual bool matchesInvalidPseudoClass() const;
virtual bool matchesReadWritePseudoClass() const;
virtual bool matchesIndeterminatePseudoClass() const;
+ virtual bool matchesDefaultPseudoClass() const;
bool matches(const String& selectors, ExceptionCode&);
Element* closest(const String& selectors, ExceptionCode&);
virtual bool shouldAppearIndeterminate() const;
@@ -405,14 +408,11 @@
virtual bool isMediaElement() const { return false; }
#endif
- virtual bool matchesValidPseudoClass() const { return false; }
- virtual bool matchesInvalidPseudoClass() const { return false; }
virtual bool isFormControlElement() const { return false; }
virtual bool isSpinButtonElement() const { return false; }
virtual bool isTextFormControl() const { return false; }
virtual bool isOptionalFormControl() const { return false; }
virtual bool isRequiredFormControl() const { return false; }
- virtual bool isDefaultButtonForForm() const { return false; }
virtual bool isInRange() const { return false; }
virtual bool isOutOfRange() const { return false; }
virtual bool isFrameElementBase() const { return false; }
Modified: trunk/Source/WebCore/html/HTMLButtonElement.cpp (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLButtonElement.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLButtonElement.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (kn...@kde.org)
* (C) 1999 Antti Koivisto (koivi...@kde.org)
* (C) 2001 Dirk Mueller (muel...@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.
* (C) 2006 Alexey Proskuryakov (a...@nypop.com)
* Copyright (C) 2007 Samuel Weinig (s...@webkit.org)
*
@@ -96,6 +96,7 @@
void HTMLButtonElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
{
if (name == typeAttr) {
+ Type oldType = m_type;
if (equalLettersIgnoringASCIICase(value, "reset"))
m_type = RESET;
else if (equalLettersIgnoringASCIICase(value, "button"))
@@ -102,7 +103,11 @@
m_type = BUTTON;
else
m_type = SUBMIT;
- setNeedsWillValidateCheck();
+ if (oldType != m_type) {
+ setNeedsWillValidateCheck();
+ if (form() && (oldType == SUBMIT || m_type == SUBMIT))
+ form()->resetDefaultButton();
+ }
} else
HTMLFormControlElement::parseAttribute(name, value);
}
@@ -164,6 +169,11 @@
return m_type == SUBMIT && !isDisabledFormControl();
}
+bool HTMLButtonElement::matchesDefaultPseudoClass() const
+{
+ return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
+}
+
bool HTMLButtonElement::isActivatedSubmit() const
{
return m_isActivatedSubmit;
Modified: trunk/Source/WebCore/html/HTMLButtonElement.h (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLButtonElement.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLButtonElement.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (kn...@kde.org)
* (C) 1999 Antti Koivisto (koivi...@kde.org)
* (C) 2000 Dirk Mueller (muel...@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.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -63,6 +63,7 @@
bool supportLabels() const override { return true; }
bool isSuccessfulSubmitButton() const override;
+ bool matchesDefaultPseudoClass() const override;
bool isActivatedSubmit() const override;
void setActivatedSubmit(bool flag) override;
Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (kn...@kde.org)
* (C) 1999 Antti Koivisto (koivi...@kde.org)
* (C) 2001 Dirk Mueller (muel...@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2016 Apple Inc. All rights reserved.
* (C) 2006 Alexey Proskuryakov (a...@nypop.com)
*
* This library is free software; you can redistribute it and/or
@@ -564,11 +564,6 @@
return FormAssociatedElement::form();
}
-bool HTMLFormControlElement::isDefaultButtonForForm() const
-{
- return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
-}
-
#if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE)
// FIXME: We should look to share this code with class HTMLFormElement instead of duplicating the logic.
Modified: trunk/Source/WebCore/html/HTMLFormControlElement.h (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLFormControlElement.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (kn...@kde.org)
* (C) 1999 Antti Koivisto (koivi...@kde.org)
* (C) 2000 Dirk Mueller (muel...@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 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
@@ -72,7 +72,6 @@
void dispatchFormControlInputEvent();
bool isDisabledFormControl() const override;
- bool isDefaultButtonForForm() const override;
bool isFocusable() const override;
bool isEnumeratable() const override { return false; }
Modified: trunk/Source/WebCore/html/HTMLFormElement.cpp (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLFormElement.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLFormElement.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -88,6 +88,7 @@
if (!shouldAutocomplete())
document().unregisterForDocumentSuspensionCallbacks(this);
+ m_defaultButton = nullptr;
for (auto& associatedElement : m_associatedElements)
associatedElement->formWillBeDestroyed();
for (auto& imageElement : m_imageElements)
@@ -593,6 +594,16 @@
void HTMLFormElement::registerFormElement(FormAssociatedElement* e)
{
m_associatedElements.insert(formElementIndex(e), e);
+
+ if (is<HTMLFormControlElement>(e)) {
+ HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*e);
+ if (control.isSuccessfulSubmitButton()) {
+ if (!m_defaultButton)
+ control.setNeedsStyleRecalc();
+ else
+ resetDefaultButton();
+ }
+ }
}
void HTMLFormElement::removeFormElement(FormAssociatedElement* e)
@@ -605,6 +616,9 @@
--m_associatedElementsAfterIndex;
removeFromPastNamesMap(e);
m_associatedElements.remove(index);
+
+ if (e == m_defaultButton)
+ resetDefaultButton();
}
void HTMLFormElement::registerInvalidAssociatedFormControl(const HTMLFormControlElement& formControlElement)
@@ -703,15 +717,38 @@
HTMLFormControlElement* HTMLFormElement::defaultButton() const
{
- for (auto& associatedElement : m_associatedElements) {
- if (!is<HTMLFormControlElement>(*associatedElement))
- continue;
- HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*associatedElement);
- if (control.isSuccessfulSubmitButton())
- return &control;
+ if (!m_defaultButton) {
+ for (auto& associatedElement : m_associatedElements) {
+ if (!is<HTMLFormControlElement>(*associatedElement))
+ continue;
+ HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*associatedElement);
+ if (control.isSuccessfulSubmitButton()) {
+ m_defaultButton = &control;
+ break;
+ }
+ }
}
+ return m_defaultButton;
+}
- return nullptr;
+void HTMLFormElement::resetDefaultButton()
+{
+ if (!m_defaultButton) {
+ // Computing the default button is not cheap, we don't want to do it unless needed.
+ // If there was no default button set, the only style to invalidate is the element
+ // being added to the form. This is done explicitely in registerFormElement().
+ return;
+ }
+
+ HTMLFormControlElement* oldDefault = m_defaultButton;
+ m_defaultButton = nullptr;
+ defaultButton();
+ if (m_defaultButton != oldDefault) {
+ if (oldDefault)
+ oldDefault->setNeedsStyleRecalc();
+ if (m_defaultButton)
+ m_defaultButton->setNeedsStyleRecalc();
+ }
}
bool HTMLFormElement::checkValidity()
Modified: trunk/Source/WebCore/html/HTMLFormElement.h (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLFormElement.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLFormElement.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -116,6 +116,7 @@
bool wasUserSubmitted() const;
HTMLFormControlElement* defaultButton() const;
+ void resetDefaultButton();
bool checkValidity();
@@ -186,6 +187,7 @@
std::unique_ptr<PastNamesMap> m_pastNamesMap;
RadioButtonGroups m_radioButtonGroups;
+ mutable HTMLFormControlElement* m_defaultButton { nullptr };
unsigned m_associatedElementsBeforeIndex;
unsigned m_associatedElementsAfterIndex;
Modified: trunk/Source/WebCore/html/HTMLInputElement.cpp (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLInputElement.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLInputElement.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -465,6 +465,7 @@
bool didStoreValue = m_inputType->storesValueSeparateFromAttribute();
bool neededSuspensionCallback = needsSuspensionCallback();
bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes();
+ bool wasSuccessfulSubmitButtonCandidate = m_inputType->canBeSuccessfulSubmitButton();
m_inputType->destroyShadowSubtree();
@@ -507,6 +508,9 @@
attributeChanged(alignAttr, nullAtom, align->value());
}
+ if (form() && wasSuccessfulSubmitButtonCandidate != m_inputType->canBeSuccessfulSubmitButton())
+ form()->resetDefaultButton();
+
runPostTypeUpdateTasks();
}
@@ -677,6 +681,9 @@
updateValidity();
m_valueAttributeWasUpdatedAfterParsing = !m_parsingInProgress;
} else if (name == checkedAttr) {
+ if (m_inputType->isCheckable())
+ setNeedsStyleRecalc();
+
// Another radio button in the same group might be checked by state
// restore. We shouldn't call setChecked() even if this has the checked
// attribute. So, delay the setChecked() call until
@@ -816,6 +823,14 @@
return !isDisabledFormControl() && m_inputType->canBeSuccessfulSubmitButton();
}
+bool HTMLInputElement::matchesDefaultPseudoClass() const
+{
+ ASSERT(m_inputType);
+ if (m_inputType->canBeSuccessfulSubmitButton())
+ return !isDisabledFormControl() && form() && form()->defaultButton() == this;
+ return m_inputType->isCheckable() && fastHasAttribute(checkedAttr);
+}
+
bool HTMLInputElement::isActivatedSubmit() const
{
return m_isActivatedSubmit;
Modified: trunk/Source/WebCore/html/HTMLInputElement.h (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLInputElement.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLInputElement.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -380,6 +380,7 @@
bool appendFormData(FormDataList&, bool) final;
bool isSuccessfulSubmitButton() const final;
+ bool matchesDefaultPseudoClass() const final;
void reset() final;
Modified: trunk/Source/WebCore/html/HTMLOptionElement.cpp (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLOptionElement.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLOptionElement.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -3,7 +3,7 @@
* (C) 1999 Antti Koivisto (koivi...@kde.org)
* (C) 2001 Dirk Mueller (muel...@kde.org)
* (C) 2006 Alexey Proskuryakov (a...@nypop.com)
- * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2010, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
* Copyright (C) 2011 Motorola Mobility, Inc. All rights reserved.
*
@@ -96,6 +96,11 @@
return style && style->display() != NONE;
}
+bool HTMLOptionElement::matchesDefaultPseudoClass() const
+{
+ return fastHasAttribute(selectedAttr);
+}
+
String HTMLOptionElement::text() const
{
String text = collectOptionInnerText();
@@ -174,6 +179,8 @@
renderer()->theme().stateChanged(*renderer(), ControlStates::EnabledState);
}
} else if (name == selectedAttr) {
+ setNeedsStyleRecalc();
+
// FIXME: This doesn't match what the HTML specification says.
// The specification implies that removing the selected attribute or
// changing the value of a selected attribute that is already present
Modified: trunk/Source/WebCore/html/HTMLOptionElement.h (202244 => 202245)
--- trunk/Source/WebCore/html/HTMLOptionElement.h 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/html/HTMLOptionElement.h 2016-06-20 21:30:36 UTC (rev 202245)
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (kn...@kde.org)
* (C) 1999 Antti Koivisto (koivi...@kde.org)
* (C) 2000 Dirk Mueller (muel...@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2010, 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
@@ -71,6 +71,7 @@
bool isFocusable() const override;
bool rendererIsNeeded(const RenderStyle&) override { return false; }
+ bool matchesDefaultPseudoClass() const override;
void parseAttribute(const QualifiedName&, const AtomicString&) override;
Modified: trunk/Source/WebCore/style/StyleSharingResolver.cpp (202244 => 202245)
--- trunk/Source/WebCore/style/StyleSharingResolver.cpp 2016-06-20 21:25:33 UTC (rev 202244)
+++ trunk/Source/WebCore/style/StyleSharingResolver.cpp 2016-06-20 21:30:36 UTC (rev 202245)
@@ -185,9 +185,6 @@
if (formElement.isDisabledFormControl() != element.isDisabledFormControl())
return false;
- if (formElement.isDefaultButtonForForm() != element.isDefaultButtonForForm())
- return false;
-
if (formElement.isInRange() != element.isInRange())
return false;
@@ -284,6 +281,9 @@
if (candidateElement.matchesIndeterminatePseudoClass() != element.matchesIndeterminatePseudoClass())
return false;
+ if (candidateElement.matchesDefaultPseudoClass() != element.matchesDefaultPseudoClass())
+ return false;
+
if (element.shadowRoot() && !element.shadowRoot()->styleResolver().ruleSets().authorStyle()->hostPseudoClassRules().isEmpty())
return false;