:in-range & :out-of-range CSS pseudo-classes shouldn't match inputs without range...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Jun 2016 22:47:02 +0000 (22:47 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Jun 2016 22:47:02 +0000 (22:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156558

Patch by Benjamin Poulain <bpoulain@apple.com> on 2016-06-16
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-expected.txt:
One of the previous match was erroneous.
Our results are still very far from being correct. There are several
bugs affecting our range validation.

Source/WebCore:

The pseudo selectors :in-range and :out-of-range should only
apply if:
-minimum/maximum are defined for the input type
-the input value is/is-not suffering from underflow/overflow.

Only certain types have a valid minimum and maximum:
-number
-range
-date
-month
-week
-time
-datetime-local

Of those, only one has a default minimum and maximum: range.
For all the others, the minimum or maximum is only defined
if the min/max attribute is defined and valid.

This patch addresses these constraints for number and range.
The date types range validation is severely broken and is
left untouched. It really needs a clean rewrite.

Tests: fast/css/pseudo-in-range-basics.html
       fast/css/pseudo-in-range-out-of-range-trivial.html
       fast/css/pseudo-out-of-range-basics.html

* html/DateInputType.cpp:
(WebCore::DateInputType::createStepRange):
* html/DateTimeInputType.cpp:
(WebCore::DateTimeInputType::createStepRange):
* html/DateTimeLocalInputType.cpp:
(WebCore::DateTimeLocalInputType::createStepRange):
* html/InputType.cpp:
(WebCore::InputType::isInRange):
(WebCore::InputType::isOutOfRange):
Notice the isEmpty() shortcut.
A value can only overflow/underflow if it is not empty.

* html/MonthInputType.cpp:
(WebCore::MonthInputType::createStepRange):
* html/NumberInputType.cpp:
(WebCore::NumberInputType::createStepRange):
* html/RangeInputType.cpp:
(WebCore::RangeInputType::createStepRange):
* html/StepRange.cpp:
(WebCore::StepRange::StepRange):
* html/StepRange.h:
(WebCore::StepRange::hasRangeLimitations):
* html/WeekInputType.cpp:
(WebCore::WeekInputType::createStepRange):

LayoutTests:

* fast/css/pseudo-in-range-basics-expected.html: Added.
* fast/css/pseudo-in-range-basics.html: Added.
* fast/css/pseudo-in-range-out-of-range-trivial-expected.html: Added.
* fast/css/pseudo-in-range-out-of-range-trivial.html: Added.
* fast/css/pseudo-out-of-range-basics-expected.html: Added.
* fast/css/pseudo-out-of-range-basics.html: Added.

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

21 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/pseudo-in-range-basics-expected.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-in-range-basics.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-in-range-out-of-range-trivial-expected.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-in-range-out-of-range-trivial.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-out-of-range-basics-expected.html [new file with mode: 0644]
LayoutTests/fast/css/pseudo-out-of-range-basics.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/html/DateInputType.cpp
Source/WebCore/html/DateTimeInputType.cpp
Source/WebCore/html/DateTimeLocalInputType.cpp
Source/WebCore/html/InputType.cpp
Source/WebCore/html/MonthInputType.cpp
Source/WebCore/html/NumberInputType.cpp
Source/WebCore/html/RangeInputType.cpp
Source/WebCore/html/StepRange.cpp
Source/WebCore/html/StepRange.h
Source/WebCore/html/TimeInputType.cpp
Source/WebCore/html/WeekInputType.cpp

index 9a2a170..034eb52 100644 (file)
@@ -1,3 +1,17 @@
+2016-06-16  Benjamin Poulain  <bpoulain@apple.com>
+
+        :in-range & :out-of-range CSS pseudo-classes shouldn't match inputs without range limitations
+        https://bugs.webkit.org/show_bug.cgi?id=156558
+
+        Reviewed by Simon Fraser.
+
+        * fast/css/pseudo-in-range-basics-expected.html: Added.
+        * fast/css/pseudo-in-range-basics.html: Added.
+        * fast/css/pseudo-in-range-out-of-range-trivial-expected.html: Added.
+        * fast/css/pseudo-in-range-out-of-range-trivial.html: Added.
+        * fast/css/pseudo-out-of-range-basics-expected.html: Added.
+        * fast/css/pseudo-out-of-range-basics.html: Added.
+
 2016-06-15  Simon Fraser  <simon.fraser@apple.com>
 
         [iOS WK2] On iPad, indirect focussing of a text field doesn't always scroll to the correct location
diff --git a/LayoutTests/fast/css/pseudo-in-range-basics-expected.html b/LayoutTests/fast/css/pseudo-in-range-basics-expected.html
new file mode 100644 (file)
index 0000000..f342396
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+      .expected {
+          background-color: red;
+          color: green;
+          border: 2px solid blue;
+          -webkit-appearance: none;
+      }
+      #testcase * {
+          margin: 0;
+          padding: 0;
+      }
+      #testcase input {
+          width: 50px;
+      }
+  </style>
+</head>
+<body>
+    <p>Check styling of elements with the :in-range pseudo selector</p>
+    <div id=testcase>
+        <div>
+            <input type="text">
+            <input type="text" min=1>
+            <input type="text" min=1 value=0>
+            <input type="text" min=1 value=1>
+            <input type="text" max=42>
+            <input type="text" max=42 value=42>
+            <input type="text" max=42 value=43>
+            <input type="text" min=1 max=42>
+            <input type="text" min=1 max=42 value=0>
+            <input type="text" min=1 max=42 value=1>
+            <input type="text" min=1 max=42 value=2>
+            <input type="text" min=1 max=42 value=41>
+            <input type="text" min=1 max=42 value=42>
+            <input type="text" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number">
+            <input type="number" min=1 class=expected>
+            <input type="number" min=1 value=0>
+            <input type="number" min=1 value=1 class=expected>
+            <input type="number" max=42 class=expected>
+            <input type="number" max=42 value=42 class=expected>
+            <input type="number" max=42 value=43>
+            <input type="number" min=1 max=42 class=expected>
+            <input type="number" min=1 max=42 value=0>
+            <input type="number" min=1 max=42 value=1 class=expected>
+            <input type="number" min=1 max=42 value=2 class=expected>
+            <input type="number" min=1 max=42 value=41 class=expected>
+            <input type="number" min=1 max=42 value=42 class=expected>
+            <input type="number" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number" min>
+            <input type="number" min value=0>
+            <input type="number" max>
+            <input type="number" max value=42>
+            <input type="number" min max>
+            <input type="number" min max=42 class=expected>
+            <input type="number" min=1 max class=expected>
+            <input type="number" min max value=0>
+            <input type="number" min=1 max value=0>
+            <input type="number" min=1 max value=1 class=expected>
+            <input type="number" min max=42 value=42 class=expected>
+            <input type="number" min max=42 value=43>
+        </div>
+        <div>
+            <input type="number" min="webkit">
+            <input type="number" min="webkit" value=0>
+            <input type="number" max="webkit">
+            <input type="number" max="webkit" value=42>
+            <input type="number" min="webkit" max="webkit">
+            <input type="number" min="webkit" max=42 class=expected>
+            <input type="number" min=1 max="webkit" class=expected>
+            <input type="number" min="webkit" max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=1 class=expected>
+            <input type="number" min="webkit" max=42 value=42 class=expected>
+            <input type="number" min="webkit" max=42 value=43>
+        </div>
+        <div>
+            <input type="range" class=expected>
+            <input type="range" min=1 class=expected>
+            <input type="range" min=1 value=0 class=expected>
+            <input type="range" min=1 value=1 class=expected>
+            <input type="range" max=42 class=expected>
+            <input type="range" max=42 value=42 class=expected>
+            <input type="range" max=42 value=43 class=expected>
+            <input type="range" min=1 max=42 class=expected>
+            <input type="range" min=1 max=42 value=0 class=expected>
+            <input type="range" min=1 max=42 value=1 class=expected>
+            <input type="range" min=1 max=42 value=2 class=expected>
+            <input type="range" min=1 max=42 value=41 class=expected>
+            <input type="range" min=1 max=42 value=42 class=expected>
+            <input type="range" min=1 max=42 value=43 class=expected>
+        </div>
+        <div>
+            <input type="range" min class=expected>
+            <input type="range" min value=0 class=expected>
+            <input type="range" max class=expected>
+            <input type="range" max value=42 class=expected>
+            <input type="range" min max class=expected>
+            <input type="range" min max=42 class=expected>
+            <input type="range" min=1 max class=expected>
+            <input type="range" min max value=0 class=expected>
+            <input type="range" min=1 max value=0 class=expected>
+            <input type="range" min=1 max value=1 class=expected>
+            <input type="range" min max=42 value=42 class=expected>
+            <input type="range" min max=42 value=43 class=expected>
+        </div>
+        <div>
+            <input type="range" min="webkit" class=expected>
+            <input type="range" min="webkit" value=0 class=expected>
+            <input type="range" max="webkit" class=expected>
+            <input type="range" max="webkit" value=42 class=expected>
+            <input type="range" min="webkit" max="webkit" class=expected>
+            <input type="range" min="webkit" max=42 class=expected>
+            <input type="range" min=1 max="webkit" class=expected>
+            <input type="range" min="webkit" max="webkit" value=0 class=expected>
+            <input type="range" min=1 max="webkit" value=0 class=expected>
+            <input type="range" min=1 max="webkit" value=1 class=expected>
+            <input type="range" min="webkit" max=42 value=42 class=expected>
+            <input type="range" min="webkit" max=42 value=43 class=expected>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-in-range-basics.html b/LayoutTests/fast/css/pseudo-in-range-basics.html
new file mode 100644 (file)
index 0000000..7f6676b
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+      :in-range {
+          background-color: red;
+          color: green;
+          border: 2px solid blue;
+          -webkit-appearance: none;
+      }
+      #testcase * {
+          margin: 0;
+          padding: 0;
+      }
+      #testcase input {
+          width: 50px;
+      }
+  </style>
+</head>
+<body>
+    <p>Check styling of elements with the :in-range pseudo selector</p>
+    <div id=testcase>
+        <div>
+            <input type="text">
+            <input type="text" min=1>
+            <input type="text" min=1 value=0>
+            <input type="text" min=1 value=1>
+            <input type="text" max=42>
+            <input type="text" max=42 value=42>
+            <input type="text" max=42 value=43>
+            <input type="text" min=1 max=42>
+            <input type="text" min=1 max=42 value=0>
+            <input type="text" min=1 max=42 value=1>
+            <input type="text" min=1 max=42 value=2>
+            <input type="text" min=1 max=42 value=41>
+            <input type="text" min=1 max=42 value=42>
+            <input type="text" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number">
+            <input type="number" min=1>
+            <input type="number" min=1 value=0>
+            <input type="number" min=1 value=1>
+            <input type="number" max=42>
+            <input type="number" max=42 value=42>
+            <input type="number" max=42 value=43>
+            <input type="number" min=1 max=42>
+            <input type="number" min=1 max=42 value=0>
+            <input type="number" min=1 max=42 value=1>
+            <input type="number" min=1 max=42 value=2>
+            <input type="number" min=1 max=42 value=41>
+            <input type="number" min=1 max=42 value=42>
+            <input type="number" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number" min>
+            <input type="number" min value=0>
+            <input type="number" max>
+            <input type="number" max value=42>
+            <input type="number" min max>
+            <input type="number" min max=42>
+            <input type="number" min=1 max>
+            <input type="number" min max value=0>
+            <input type="number" min=1 max value=0>
+            <input type="number" min=1 max value=1>
+            <input type="number" min max=42 value=42>
+            <input type="number" min max=42 value=43>
+        </div>
+        <div>
+            <input type="number" min="webkit">
+            <input type="number" min="webkit" value=0>
+            <input type="number" max="webkit">
+            <input type="number" max="webkit" value=42>
+            <input type="number" min="webkit" max="webkit">
+            <input type="number" min="webkit" max=42>
+            <input type="number" min=1 max="webkit">
+            <input type="number" min="webkit" max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=1>
+            <input type="number" min="webkit" max=42 value=42>
+            <input type="number" min="webkit" max=42 value=43>
+        </div>
+        <div>
+            <input type="range">
+            <input type="range" min=1>
+            <input type="range" min=1 value=0>
+            <input type="range" min=1 value=1>
+            <input type="range" max=42>
+            <input type="range" max=42 value=42>
+            <input type="range" max=42 value=43>
+            <input type="range" min=1 max=42>
+            <input type="range" min=1 max=42 value=0>
+            <input type="range" min=1 max=42 value=1>
+            <input type="range" min=1 max=42 value=2>
+            <input type="range" min=1 max=42 value=41>
+            <input type="range" min=1 max=42 value=42>
+            <input type="range" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="range" min>
+            <input type="range" min value=0>
+            <input type="range" max>
+            <input type="range" max value=42>
+            <input type="range" min max>
+            <input type="range" min max=42>
+            <input type="range" min=1 max>
+            <input type="range" min max value=0>
+            <input type="range" min=1 max value=0>
+            <input type="range" min=1 max value=1>
+            <input type="range" min max=42 value=42>
+            <input type="range" min max=42 value=43>
+        </div>
+        <div>
+            <input type="range" min="webkit">
+            <input type="range" min="webkit" value=0>
+            <input type="range" max="webkit">
+            <input type="range" max="webkit" value=42>
+            <input type="range" min="webkit" max="webkit">
+            <input type="range" min="webkit" max=42>
+            <input type="range" min=1 max="webkit">
+            <input type="range" min="webkit" max="webkit" value=0>
+            <input type="range" min=1 max="webkit" value=0>
+            <input type="range" min=1 max="webkit" value=1>
+            <input type="range" min="webkit" max=42 value=42>
+            <input type="range" min="webkit" max=42 value=43>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-in-range-out-of-range-trivial-expected.html b/LayoutTests/fast/css/pseudo-in-range-out-of-range-trivial-expected.html
new file mode 100644 (file)
index 0000000..53ded06
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+      :in-range, :out-of-range {
+          background-color: red;
+          outline: 2px solid red;
+      }
+  </style>
+</head>
+<body>
+  <!-- Chris Rebert's test case for https://bugs.webkit.org/show_bug.cgi?id=156558 -->
+  <p>Verify that neither in-range, no out-of-range match elements without range limitations.</p>
+  <p>Test passes if there is no red:</p>
+  <input type="number" value="5"> number
+  <br><br>
+  <input type="date" value="2010-10-10" id="date"> date
+  <br><br>
+  <input type="time" value="02:00:00" id="time"> time
+  <br><br>
+  <input type="week" value="2016-W07" id="week"> week
+  <br><br>
+  <input type="month" value="2000-06" id="month"> month
+  <br><br>
+  <input type="datetime-local" value="2012-11-28T23:59:59" id="datetime-local"> datetime-local
+  <br><br>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-in-range-out-of-range-trivial.html b/LayoutTests/fast/css/pseudo-in-range-out-of-range-trivial.html
new file mode 100644 (file)
index 0000000..53ded06
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+      :in-range, :out-of-range {
+          background-color: red;
+          outline: 2px solid red;
+      }
+  </style>
+</head>
+<body>
+  <!-- Chris Rebert's test case for https://bugs.webkit.org/show_bug.cgi?id=156558 -->
+  <p>Verify that neither in-range, no out-of-range match elements without range limitations.</p>
+  <p>Test passes if there is no red:</p>
+  <input type="number" value="5"> number
+  <br><br>
+  <input type="date" value="2010-10-10" id="date"> date
+  <br><br>
+  <input type="time" value="02:00:00" id="time"> time
+  <br><br>
+  <input type="week" value="2016-W07" id="week"> week
+  <br><br>
+  <input type="month" value="2000-06" id="month"> month
+  <br><br>
+  <input type="datetime-local" value="2012-11-28T23:59:59" id="datetime-local"> datetime-local
+  <br><br>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-out-of-range-basics-expected.html b/LayoutTests/fast/css/pseudo-out-of-range-basics-expected.html
new file mode 100644 (file)
index 0000000..8788c55
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+      .expected {
+          background-color: red;
+          color: green;
+          border: 2px solid blue;
+          -webkit-appearance: none;
+      }
+      #testcase * {
+          margin: 0;
+          padding: 0;
+      }
+      #testcase input {
+          width: 50px;
+      }
+  </style>
+</head>
+<body>
+    <p>Check styling of elements with the :in-range pseudo selector</p>
+    <div id=testcase>
+        <div>
+            <input type="text">
+            <input type="text" min=1>
+            <input type="text" min=1 value=0>
+            <input type="text" min=1 value=1>
+            <input type="text" max=42>
+            <input type="text" max=42 value=42>
+            <input type="text" max=42 value=43>
+            <input type="text" min=1 max=42>
+            <input type="text" min=1 max=42 value=0>
+            <input type="text" min=1 max=42 value=1>
+            <input type="text" min=1 max=42 value=2>
+            <input type="text" min=1 max=42 value=41>
+            <input type="text" min=1 max=42 value=42>
+            <input type="text" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number">
+            <input type="number" min=1>
+            <input type="number" min=1 value=0 class=expected>
+            <input type="number" min=1 value=1>
+            <input type="number" max=42>
+            <input type="number" max=42 value=42>
+            <input type="number" max=42 value=43 class=expected>
+            <input type="number" min=1 max=42>
+            <input type="number" min=1 max=42 value=0 class=expected>
+            <input type="number" min=1 max=42 value=1>
+            <input type="number" min=1 max=42 value=2>
+            <input type="number" min=1 max=42 value=41>
+            <input type="number" min=1 max=42 value=42>
+            <input type="number" min=1 max=42 value=43 class=expected>
+        </div>
+        <div>
+            <input type="number" min>
+            <input type="number" min value=0>
+            <input type="number" max>
+            <input type="number" max value=42>
+            <input type="number" min max>
+            <input type="number" min max=42>
+            <input type="number" min=1 max>
+            <input type="number" min max value=0>
+            <input type="number" min=1 max value=0 class=expected>
+            <input type="number" min=1 max value=1>
+            <input type="number" min max=42 value=42>
+            <input type="number" min max=42 value=43 class=expected>
+        </div>
+        <div>
+            <input type="number" min="webkit">
+            <input type="number" min="webkit" value=0>
+            <input type="number" max="webkit">
+            <input type="number" max="webkit" value=42>
+            <input type="number" min="webkit" max="webkit">
+            <input type="number" min="webkit" max=42>
+            <input type="number" min=1 max="webkit">
+            <input type="number" min="webkit" max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=0 class=expected>
+            <input type="number" min=1 max="webkit" value=1>
+            <input type="number" min="webkit" max=42 value=42>
+            <input type="number" min="webkit" max=42 value=43 class=expected>
+        </div>
+        <div>
+            <input type="range">
+            <input type="range" min=1>
+            <input type="range" min=1 value=0>
+            <input type="range" min=1 value=1>
+            <input type="range" max=42>
+            <input type="range" max=42 value=42>
+            <input type="range" max=42 value=43>
+            <input type="range" min=1 max=42>
+            <input type="range" min=1 max=42 value=0>
+            <input type="range" min=1 max=42 value=1>
+            <input type="range" min=1 max=42 value=2>
+            <input type="range" min=1 max=42 value=41>
+            <input type="range" min=1 max=42 value=42>
+            <input type="range" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="range" min>
+            <input type="range" min value=0>
+            <input type="range" max>
+            <input type="range" max value=42>
+            <input type="range" min max>
+            <input type="range" min max=42>
+            <input type="range" min=1 max>
+            <input type="range" min max value=0>
+            <input type="range" min=1 max value=0>
+            <input type="range" min=1 max value=1>
+            <input type="range" min max=42 value=42>
+            <input type="range" min max=42 value=43>
+        </div>
+        <div>
+            <input type="range" min="webkit">
+            <input type="range" min="webkit" value=0>
+            <input type="range" max="webkit">
+            <input type="range" max="webkit" value=42>
+            <input type="range" min="webkit" max="webkit">
+            <input type="range" min="webkit" max=42>
+            <input type="range" min=1 max="webkit">
+            <input type="range" min="webkit" max="webkit" value=0>
+            <input type="range" min=1 max="webkit" value=0>
+            <input type="range" min=1 max="webkit" value=1>
+            <input type="range" min="webkit" max=42 value=42>
+            <input type="range" min="webkit" max=42 value=43>
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/css/pseudo-out-of-range-basics.html b/LayoutTests/fast/css/pseudo-out-of-range-basics.html
new file mode 100644 (file)
index 0000000..8d174ae
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+      :out-of-range {
+          background-color: red;
+          color: green;
+          border: 2px solid blue;
+          -webkit-appearance: none;
+      }
+      #testcase * {
+          margin: 0;
+          padding: 0;
+      }
+      #testcase input {
+          width: 50px;
+      }
+  </style>
+</head>
+<body>
+    <p>Check styling of elements with the :in-range pseudo selector</p>
+    <div id=testcase>
+        <div>
+            <input type="text">
+            <input type="text" min=1>
+            <input type="text" min=1 value=0>
+            <input type="text" min=1 value=1>
+            <input type="text" max=42>
+            <input type="text" max=42 value=42>
+            <input type="text" max=42 value=43>
+            <input type="text" min=1 max=42>
+            <input type="text" min=1 max=42 value=0>
+            <input type="text" min=1 max=42 value=1>
+            <input type="text" min=1 max=42 value=2>
+            <input type="text" min=1 max=42 value=41>
+            <input type="text" min=1 max=42 value=42>
+            <input type="text" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number">
+            <input type="number" min=1>
+            <input type="number" min=1 value=0>
+            <input type="number" min=1 value=1>
+            <input type="number" max=42>
+            <input type="number" max=42 value=42>
+            <input type="number" max=42 value=43>
+            <input type="number" min=1 max=42>
+            <input type="number" min=1 max=42 value=0>
+            <input type="number" min=1 max=42 value=1>
+            <input type="number" min=1 max=42 value=2>
+            <input type="number" min=1 max=42 value=41>
+            <input type="number" min=1 max=42 value=42>
+            <input type="number" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="number" min>
+            <input type="number" min value=0>
+            <input type="number" max>
+            <input type="number" max value=42>
+            <input type="number" min max>
+            <input type="number" min max=42>
+            <input type="number" min=1 max>
+            <input type="number" min max value=0>
+            <input type="number" min=1 max value=0>
+            <input type="number" min=1 max value=1>
+            <input type="number" min max=42 value=42>
+            <input type="number" min max=42 value=43>
+        </div>
+        <div>
+            <input type="number" min="webkit">
+            <input type="number" min="webkit" value=0>
+            <input type="number" max="webkit">
+            <input type="number" max="webkit" value=42>
+            <input type="number" min="webkit" max="webkit">
+            <input type="number" min="webkit" max=42>
+            <input type="number" min=1 max="webkit">
+            <input type="number" min="webkit" max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=0>
+            <input type="number" min=1 max="webkit" value=1>
+            <input type="number" min="webkit" max=42 value=42>
+            <input type="number" min="webkit" max=42 value=43>
+        </div>
+        <div>
+            <input type="range">
+            <input type="range" min=1>
+            <input type="range" min=1 value=0>
+            <input type="range" min=1 value=1>
+            <input type="range" max=42>
+            <input type="range" max=42 value=42>
+            <input type="range" max=42 value=43>
+            <input type="range" min=1 max=42>
+            <input type="range" min=1 max=42 value=0>
+            <input type="range" min=1 max=42 value=1>
+            <input type="range" min=1 max=42 value=2>
+            <input type="range" min=1 max=42 value=41>
+            <input type="range" min=1 max=42 value=42>
+            <input type="range" min=1 max=42 value=43>
+        </div>
+        <div>
+            <input type="range" min>
+            <input type="range" min value=0>
+            <input type="range" max>
+            <input type="range" max value=42>
+            <input type="range" min max>
+            <input type="range" min max=42>
+            <input type="range" min=1 max>
+            <input type="range" min max value=0>
+            <input type="range" min=1 max value=0>
+            <input type="range" min=1 max value=1>
+            <input type="range" min max=42 value=42>
+            <input type="range" min max=42 value=43>
+        </div>
+        <div>
+            <input type="range" min="webkit">
+            <input type="range" min="webkit" value=0>
+            <input type="range" max="webkit">
+            <input type="range" max="webkit" value=42>
+            <input type="range" min="webkit" max="webkit">
+            <input type="range" min="webkit" max=42>
+            <input type="range" min=1 max="webkit">
+            <input type="range" min="webkit" max="webkit" value=0>
+            <input type="range" min=1 max="webkit" value=0>
+            <input type="range" min=1 max="webkit" value=1>
+            <input type="range" min="webkit" max=42 value=42>
+            <input type="range" min="webkit" max=42 value=43>
+        </div>
+    </div>
+</body>
+</html>
index ca37241..bd1d6dc 100644 (file)
@@ -1,3 +1,15 @@
+2016-06-16  Benjamin Poulain  <bpoulain@apple.com>
+
+        :in-range & :out-of-range CSS pseudo-classes shouldn't match inputs without range limitations
+        https://bugs.webkit.org/show_bug.cgi?id=156558
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-expected.txt:
+        One of the previous match was erroneous.
+        Our results are still very far from being correct. There are several
+        bugs affecting our range validation.
+
 2016-06-13  Joseph Pecoraro  <pecoraro@apple.com>
 
         window.onerror should pass the ErrorEvent's 'error' property as the 5th argument to the event handler
index a0b5bd6..8096f56 100644 (file)
@@ -1,9 +1,9 @@
                                                     
 
-FAIL ':in-range' matches all elements that are candidates for constraint validation, have range limitations, and that are neither suffering from an underflow nor suffering from an overflow assert_array_equals: lengths differ, expected 10 got 8
+FAIL ':in-range' matches all elements that are candidates for constraint validation, have range limitations, and that are neither suffering from an underflow nor suffering from an overflow assert_array_equals: lengths differ, expected 10 got 7
 FAIL ':out-of-range' matches all elements that are candidates for constraint validation, have range limitations, and that are either suffering from an underflow or suffering from an overflow assert_array_equals: lengths differ, expected 12 got 2
-FAIL ':in-range' update number1's value < min assert_array_equals: lengths differ, expected 9 got 7
+FAIL ':in-range' update number1's value < min assert_array_equals: lengths differ, expected 9 got 6
 FAIL ':out-of-range' update number1's value < min assert_array_equals: lengths differ, expected 13 got 3
-FAIL ':in-range' update number3's min < value assert_array_equals: lengths differ, expected 10 got 8
+FAIL ':in-range' update number3's min < value assert_array_equals: lengths differ, expected 10 got 7
 FAIL ':out-of-range' update number3's min < value assert_array_equals: lengths differ, expected 12 got 2
 
index c780c18..706fb7d 100644 (file)
@@ -1,3 +1,61 @@
+2016-06-16  Benjamin Poulain  <bpoulain@apple.com>
+
+        :in-range & :out-of-range CSS pseudo-classes shouldn't match inputs without range limitations
+        https://bugs.webkit.org/show_bug.cgi?id=156558
+
+        Reviewed by Simon Fraser.
+
+        The pseudo selectors :in-range and :out-of-range should only
+        apply if:
+        -minimum/maximum are defined for the input type
+        -the input value is/is-not suffering from underflow/overflow.
+
+        Only certain types have a valid minimum and maximum:
+        -number
+        -range
+        -date
+        -month
+        -week
+        -time
+        -datetime-local
+
+        Of those, only one has a default minimum and maximum: range.
+        For all the others, the minimum or maximum is only defined
+        if the min/max attribute is defined and valid.
+
+        This patch addresses these constraints for number and range.
+        The date types range validation is severely broken and is
+        left untouched. It really needs a clean rewrite.
+
+        Tests: fast/css/pseudo-in-range-basics.html
+               fast/css/pseudo-in-range-out-of-range-trivial.html
+               fast/css/pseudo-out-of-range-basics.html
+
+        * html/DateInputType.cpp:
+        (WebCore::DateInputType::createStepRange):
+        * html/DateTimeInputType.cpp:
+        (WebCore::DateTimeInputType::createStepRange):
+        * html/DateTimeLocalInputType.cpp:
+        (WebCore::DateTimeLocalInputType::createStepRange):
+        * html/InputType.cpp:
+        (WebCore::InputType::isInRange):
+        (WebCore::InputType::isOutOfRange):
+        Notice the isEmpty() shortcut.
+        A value can only overflow/underflow if it is not empty.
+
+        * html/MonthInputType.cpp:
+        (WebCore::MonthInputType::createStepRange):
+        * html/NumberInputType.cpp:
+        (WebCore::NumberInputType::createStepRange):
+        * html/RangeInputType.cpp:
+        (WebCore::RangeInputType::createStepRange):
+        * html/StepRange.cpp:
+        (WebCore::StepRange::StepRange):
+        * html/StepRange.h:
+        (WebCore::StepRange::hasRangeLimitations):
+        * html/WeekInputType.cpp:
+        (WebCore::WeekInputType::createStepRange):
+
 2016-06-16  Anders Carlsson  <andersca@apple.com>
 
         Fix macOS Sierra build
index b34f7d8..b553f9f 100644 (file)
@@ -68,7 +68,7 @@ StepRange DateInputType::createStepRange(AnyStepHandling anyStepHandling) const
     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumDate()));
     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumDate()));
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    return StepRange(stepBase, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 bool DateInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const
index b294514..ec53508 100644 (file)
@@ -69,7 +69,7 @@ StepRange DateTimeInputType::createStepRange(AnyStepHandling anyStepHandling) co
     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumDateTime()));
     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumDateTime()));
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    return StepRange(stepBase, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 bool DateTimeInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const
index 821c811..dd895f6 100644 (file)
@@ -75,7 +75,7 @@ StepRange DateTimeLocalInputType::createStepRange(AnyStepHandling anyStepHandlin
     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumDateTime()));
     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumDateTime()));
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    return StepRange(stepBase, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 bool DateTimeLocalInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const
index 41199b5..5836321 100644 (file)
@@ -339,24 +339,30 @@ bool InputType::isInRange(const String& value) const
     if (!isSteppable())
         return false;
 
+    StepRange stepRange(createStepRange(RejectAny));
+    if (!stepRange.hasRangeLimitations())
+        return false;
+    
     const Decimal numericValue = parseToNumberOrNaN(value);
     if (!numericValue.isFinite())
         return true;
 
-    StepRange stepRange(createStepRange(RejectAny));
     return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
 }
 
 bool InputType::isOutOfRange(const String& value) const
 {
-    if (!isSteppable())
+    if (!isSteppable() || value.isEmpty())
+        return false;
+
+    StepRange stepRange(createStepRange(RejectAny));
+    if (!stepRange.hasRangeLimitations())
         return false;
 
     const Decimal numericValue = parseToNumberOrNaN(value);
     if (!numericValue.isFinite())
         return true;
 
-    StepRange stepRange(createStepRange(RejectAny));
     return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
 }
 
index 60c3168..a79bbba 100644 (file)
@@ -97,7 +97,7 @@ StepRange MonthInputType::createStepRange(AnyStepHandling anyStepHandling) const
     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumMonth()));
     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumMonth()));
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    return StepRange(stepBase, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 Decimal MonthInputType::parseToNumber(const String& src, const Decimal& defaultValue) const
index 51d24e7..bfa8c41 100644 (file)
@@ -156,10 +156,23 @@ StepRange NumberInputType::createStepRange(AnyStepHandling anyStepHandling) cons
     const Decimal stepBase = parseToDecimalForNumberType(element().fastGetAttribute(minAttr), numberDefaultStepBase);
     // FIXME: We should use numeric_limits<double>::max for number input type.
     const Decimal floatMax = Decimal::fromDouble(std::numeric_limits<float>::max());
-    const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), -floatMax);
-    const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), floatMax);
-    const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    const Element& element = this->element();
+
+    RangeLimitations rangeLimitations = RangeLimitations::Invalid;
+    auto extractBound = [&] (const QualifiedName& attributeName, const Decimal& defaultValue) -> Decimal {
+        const AtomicString& attributeValue = element.fastGetAttribute(attributeName);
+        Decimal valueFromAttribute = parseToNumberOrNaN(attributeValue);
+        if (valueFromAttribute.isFinite()) {
+            rangeLimitations = RangeLimitations::Valid;
+            return valueFromAttribute;
+        }
+        return defaultValue;
+    };
+    Decimal minimum = extractBound(minAttr, -floatMax);
+    Decimal maximum = extractBound(maxAttr, floatMax);
+
+    const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element.fastGetAttribute(stepAttr));
+    return StepRange(stepBase, rangeLimitations, minimum, maximum, step, stepDescription);
 }
 
 bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const
index 0bb9906..a82b54f 100644 (file)
@@ -123,11 +123,11 @@ StepRange RangeInputType::createStepRange(AnyStepHandling anyStepHandling) const
     const AtomicString& precisionValue = element().fastGetAttribute(precisionAttr);
     if (!precisionValue.isNull()) {
         const Decimal step = equalLettersIgnoringASCIICase(precisionValue, "float") ? Decimal::nan() : 1;
-        return StepRange(minimum, minimum, maximum, step, stepDescription);
+        return StepRange(minimum, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
     }
 
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(minimum, minimum, maximum, step, stepDescription);
+    return StepRange(minimum, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 bool RangeInputType::isSteppable() const
index c0cb4fe..76d21dc 100644 (file)
@@ -35,7 +35,6 @@ StepRange::StepRange()
     , m_minimum(0)
     , m_step(1)
     , m_stepBase(0)
-    , m_hasStep(false)
 {
 }
 
@@ -45,16 +44,18 @@ StepRange::StepRange(const StepRange& stepRange)
     , m_step(stepRange.m_step)
     , m_stepBase(stepRange.m_stepBase)
     , m_stepDescription(stepRange.m_stepDescription)
+    , m_hasRangeLimitations(stepRange.m_hasRangeLimitations)
     , m_hasStep(stepRange.m_hasStep)
 {
 }
 
-StepRange::StepRange(const Decimal& stepBase, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription& stepDescription)
+StepRange::StepRange(const Decimal& stepBase, RangeLimitations rangeLimitations, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription& stepDescription)
     : m_maximum(maximum)
     , m_minimum(minimum)
     , m_step(step.isFinite() ? step : 1)
     , m_stepBase(stepBase.isFinite() ? stepBase : 1)
     , m_stepDescription(stepDescription)
+    , m_hasRangeLimitations(rangeLimitations == RangeLimitations::Valid)
     , m_hasStep(step.isFinite())
 {
     ASSERT(m_maximum.isFinite());
index 697a7b5..c62a15b 100644 (file)
@@ -31,6 +31,11 @@ class HTMLInputElement;
 
 enum AnyStepHandling { RejectAny, AnyIsDefaultStep };
 
+enum class RangeLimitations {
+    Valid,
+    Invalid
+};
+
 class StepRange {
 public:
     enum StepValueShouldBe {
@@ -71,11 +76,12 @@ public:
 
     StepRange();
     StepRange(const StepRange&);
-    StepRange(const Decimal& stepBase, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription&);
+    StepRange(const Decimal& stepBase, RangeLimitations, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription&);
     Decimal acceptableError() const;
     Decimal alignValueForStep(const Decimal& currentValue, const Decimal& newValue) const;
     Decimal clampValue(const Decimal& value) const;
     bool hasStep() const { return m_hasStep; }
+    bool hasRangeLimitations() const { return m_hasRangeLimitations; }
     Decimal maximum() const { return m_maximum; }
     Decimal minimum() const { return m_minimum; }
     static Decimal parseStep(AnyStepHandling, const StepDescription&, const String&);
@@ -114,7 +120,8 @@ private:
     const Decimal m_step;
     const Decimal m_stepBase;
     const StepDescription m_stepDescription;
-    const bool m_hasStep;
+    const bool m_hasRangeLimitations { false };
+    const bool m_hasStep { false };
 };
 
 }
index 74b14af..cf1ac72 100644 (file)
@@ -84,7 +84,7 @@ StepRange TimeInputType::createStepRange(AnyStepHandling anyStepHandling) const
     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumTime()));
     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumTime()));
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    return StepRange(stepBase, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 bool TimeInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const
index 23b90cd..9ff0290 100644 (file)
@@ -63,7 +63,7 @@ StepRange WeekInputType::createStepRange(AnyStepHandling anyStepHandling) const
     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumWeek()));
     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumWeek()));
     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
-    return StepRange(stepBase, minimum, maximum, step, stepDescription);
+    return StepRange(stepBase, RangeLimitations::Valid, minimum, maximum, step, stepDescription);
 }
 
 bool WeekInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const