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

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

<h3>Log Message</h3>
<pre>&lt;input type=&quot;range&quot;&gt; changing to disabled while active breaks all pointer events
https://bugs.webkit.org/show_bug.cgi?id=170447
&lt;rdar://problem/31442875&gt;

Reviewed by Geoffrey Garen.

Source/WebCore:

When a range's slider is being moved, we set SliderThumbElement's m_inDragMode flag
to true and mark the range elements as the CapturingMouseEventsElement. When we get
the mouseUp event, we are supposed to exit drag mode. However, when the range element
gets disabled while dragging, we do not get the mouseUp event and we need to make
sure we exit dragging mode anyway. <a href="http://trac.webkit.org/projects/webkit/changeset/112547">r112547</a> tried to fix this by calling stopDragging()
in SliderThumbElement::defaultEventHandler() when the input element is disabled.
While this often works, this is fragile and we sometimes fail to exit dragging mode
when we should.

This patch addressed the issue by calling stopDragging() in
SliderThumbElement::disabledAttributeChanged() instead. This is much safer as we
guarantee will exit dragging mode whenever the range element gets disabled, even
if SliderThumbElement::defaultEventHandler() does not get called after that.

Test: fast/forms/range/disabled-while-dragging.html

* html/RangeInputType.cpp:
(WebCore::RangeInputType::disabledAttributeChanged):
* html/RangeInputType.h:
* html/shadow/SliderThumbElement.cpp:
(WebCore::SliderThumbElement::defaultEventHandler):
(WebCore::SliderThumbElement::disabledAttributeChanged):
* html/shadow/SliderThumbElement.h:

LayoutTests:

Add layout test coverage.

* fast/forms/range/disabled-while-dragging-expected.txt: Added.
* fast/forms/range/disabled-while-dragging.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformiosTestExpectations">trunk/LayoutTests/platform/ios/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorehtmlRangeInputTypecpp">trunk/Source/WebCore/html/RangeInputType.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlRangeInputTypeh">trunk/Source/WebCore/html/RangeInputType.h</a></li>
<li><a href="#trunkSourceWebCorehtmlshadowSliderThumbElementcpp">trunk/Source/WebCore/html/shadow/SliderThumbElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlshadowSliderThumbElementh">trunk/Source/WebCore/html/shadow/SliderThumbElement.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastformsrangedisabledwhiledraggingexpectedtxt">trunk/LayoutTests/fast/forms/range/disabled-while-dragging-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastformsrangedisabledwhiledragginghtml">trunk/LayoutTests/fast/forms/range/disabled-while-dragging.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/LayoutTests/ChangeLog        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2017-04-05  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        &lt;input type=&quot;range&quot;&gt; changing to disabled while active breaks all pointer events
+        https://bugs.webkit.org/show_bug.cgi?id=170447
+        &lt;rdar://problem/31442875&gt;
+
+        Reviewed by Geoffrey Garen.
+
+        Add layout test coverage.
+
+        * fast/forms/range/disabled-while-dragging-expected.txt: Added.
+        * fast/forms/range/disabled-while-dragging.html: Added.
+
</ins><span class="cx"> 2017-04-05  Jiewen Tan  &lt;jiewen_tan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rebasing crypto/subtle/rsa-import-key-malformed-parameters.html
</span></span></pre></div>
<a id="trunkLayoutTestsfastformsrangedisabledwhiledraggingexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/forms/range/disabled-while-dragging-expected.txt (0 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/forms/range/disabled-while-dragging-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/forms/range/disabled-while-dragging-expected.txt        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -0,0 +1,14 @@
</span><ins>+Tests that click events still work after a range is disabled while dragging.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS range.disabled is false
+Drag range slider.
+PASS range.disabled is true
+Click button
+PASS buttonClicked is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ Click
</ins></span></pre></div>
<a id="trunkLayoutTestsfastformsrangedisabledwhiledragginghtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/forms/range/disabled-while-dragging.html (0 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/forms/range/disabled-while-dragging.html                                (rev 0)
+++ trunk/LayoutTests/fast/forms/range/disabled-while-dragging.html        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -0,0 +1,59 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+description(&quot;Tests that click events still work after a range is disabled while dragging.&quot;);
+jsTestIsAsync = true;
+
+const input = (that) =&gt; {
+    if (Math.abs(that.value - 50) === 50)
+        that.disabled = true;
+};
+
+let buttonClicked = false;
+
+function buttonClick()
+{
+    buttonClicked = true;
+}
+
+onload = function() {
+    range = document.querySelector(&quot;input&quot;);
+
+    shouldBeFalse(&quot;range.disabled&quot;);
+
+    debug(&quot;Drag range slider.&quot;);
+    var centerY = range.offsetTop + range.offsetHeight / 2;
+    var centerX = range.offsetLeft + range.offsetWidth / 2;
+    var rightEdgeX = range.offsetLeft + range.offsetWidth - 1;
+
+    eventSender.mouseMoveTo(centerX, centerY);
+    eventSender.mouseDown();
+    eventSender.mouseMoveTo(rightEdgeX, centerY);
+    eventSender.mouseUp();
+
+    setTimeout(function() {
+        shouldBeTrue(&quot;range.disabled&quot;);
+
+        debug(&quot;Click button&quot;);
+        button = document.querySelector(&quot;button&quot;);
+        var centerY = button.offsetTop + button.offsetHeight / 2;
+        var centerX = button.offsetLeft + button.offsetWidth / 2;
+        eventSender.mouseMoveTo(centerX, centerY);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+
+        setTimeout(function() {
+            shouldBeTrue(&quot;buttonClicked&quot;);
+            finishJSTest();
+        }, 0);
+    }, 0);
+}
+&lt;/script&gt;
+
+&lt;input type=&quot;range&quot; oninput=&quot;input(this)&quot;&gt;
+&lt;button onclick=&quot;buttonClick()&quot;&gt;Click&lt;/button&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformiosTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios/TestExpectations (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios/TestExpectations        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/LayoutTests/platform/ios/TestExpectations        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -380,6 +380,8 @@
</span><span class="cx"> media/audio-dealloc-crash.html [ Skip ]
</span><span class="cx"> 
</span><span class="cx"> # This test uses EventSender's mouseMoveTo, mouseUp and mouseDown
</span><ins>+fast/forms/range/disabled-while-dragging.html [ Skip ]
+fast/forms/range/range-drag-when-toggled-disabled.html [ Skip ]
</ins><span class="cx"> fast/media/video-element-in-details-collapse.html [ Skip ]
</span><span class="cx"> 
</span><span class="cx"> # The file-wrapper part of &lt;attachment&gt; is not yet working on iOS
</span><span class="lines">@@ -1421,7 +1423,6 @@
</span><span class="cx"> fast/forms/password-placeholder-text-security.html [ Pass ImageOnlyFailure ]
</span><span class="cx"> fast/forms/placeholder-position.html [ Failure ]
</span><span class="cx"> fast/forms/radio/indeterminate-radio.html [ Pass Failure ]
</span><del>-fast/forms/range/range-drag-when-toggled-disabled.html [ Failure ]
</del><span class="cx"> fast/forms/range/range-drag.html [ Failure ]
</span><span class="cx"> fast/forms/range/range-hit-test-with-padding.html [ Failure ]
</span><span class="cx"> fast/forms/range/range-slow-drag-to-edge.html [ Failure ]
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/Source/WebCore/ChangeLog        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2017-04-05  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        &lt;input type=&quot;range&quot;&gt; changing to disabled while active breaks all pointer events
+        https://bugs.webkit.org/show_bug.cgi?id=170447
+        &lt;rdar://problem/31442875&gt;
+
+        Reviewed by Geoffrey Garen.
+
+        When a range's slider is being moved, we set SliderThumbElement's m_inDragMode flag
+        to true and mark the range elements as the CapturingMouseEventsElement. When we get
+        the mouseUp event, we are supposed to exit drag mode. However, when the range element
+        gets disabled while dragging, we do not get the mouseUp event and we need to make
+        sure we exit dragging mode anyway. r112547 tried to fix this by calling stopDragging()
+        in SliderThumbElement::defaultEventHandler() when the input element is disabled.
+        While this often works, this is fragile and we sometimes fail to exit dragging mode
+        when we should.
+
+        This patch addressed the issue by calling stopDragging() in
+        SliderThumbElement::disabledAttributeChanged() instead. This is much safer as we
+        guarantee will exit dragging mode whenever the range element gets disabled, even
+        if SliderThumbElement::defaultEventHandler() does not get called after that.
+
+        Test: fast/forms/range/disabled-while-dragging.html
+
+        * html/RangeInputType.cpp:
+        (WebCore::RangeInputType::disabledAttributeChanged):
+        * html/RangeInputType.h:
+        * html/shadow/SliderThumbElement.cpp:
+        (WebCore::SliderThumbElement::defaultEventHandler):
+        (WebCore::SliderThumbElement::disabledAttributeChanged):
+        * html/shadow/SliderThumbElement.h:
+
</ins><span class="cx"> 2017-04-05  Eric Carlson  &lt;eric.carlson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [MediaStream] Video doesn't render in fullscreen on iOS
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlRangeInputTypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/RangeInputType.cpp (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/RangeInputType.cpp        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/Source/WebCore/html/RangeInputType.cpp        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -182,14 +182,12 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><ins>+#endif // ENABLE(TOUCH_EVENTS)
</ins><span class="cx"> 
</span><del>-#if PLATFORM(IOS)
</del><span class="cx"> void RangeInputType::disabledAttributeChanged()
</span><span class="cx"> {
</span><span class="cx">     typedSliderThumbElement().disabledAttributeChanged();
</span><span class="cx"> }
</span><del>-#endif
-#endif // ENABLE(TOUCH_EVENTS)
</del><span class="cx"> 
</span><span class="cx"> void RangeInputType::handleKeydownEvent(KeyboardEvent&amp; event)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlRangeInputTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/RangeInputType.h (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/RangeInputType.h        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/Source/WebCore/html/RangeInputType.h        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -81,9 +81,7 @@
</span><span class="cx">     void handleTouchEvent(TouchEvent&amp;) final;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-#if ENABLE(TOUCH_EVENTS) &amp;&amp; PLATFORM(IOS)
</del><span class="cx">     void disabledAttributeChanged() final;
</span><del>-#endif
</del><span class="cx"> 
</span><span class="cx"> #if ENABLE(TOUCH_EVENTS) &amp;&amp; !PLATFORM(IOS) &amp;&amp; ENABLE(TOUCH_SLIDER)
</span><span class="cx">     bool hasTouchEventHandler() const final;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlshadowSliderThumbElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/shadow/SliderThumbElement.cpp (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/shadow/SliderThumbElement.cpp        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/Source/WebCore/html/shadow/SliderThumbElement.cpp        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -349,7 +349,6 @@
</span><span class="cx">     // Missing this kind of check is likely to occur elsewhere if adding it in each shadow element.
</span><span class="cx">     HTMLInputElement* input = hostInput();
</span><span class="cx">     if (!input || input-&gt;isDisabledFormControl()) {
</span><del>-        stopDragging();
</del><span class="cx">         HTMLDivElement::defaultEventHandler(event);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -564,15 +563,20 @@
</span><span class="cx">     document().removeTouchEventHandler(*this);
</span><span class="cx">     m_isRegisteredAsTouchEventListener = false;
</span><span class="cx"> }
</span><ins>+#endif // ENABLE(IOS_TOUCH_EVENTS)
</ins><span class="cx"> 
</span><span class="cx"> void SliderThumbElement::disabledAttributeChanged()
</span><span class="cx"> {
</span><ins>+    if (isDisabledFormControl())
+        stopDragging();
+
+#if ENABLE(IOS_TOUCH_EVENTS)
</ins><span class="cx">     if (shouldAcceptTouchEvents())
</span><span class="cx">         registerForTouchEvents();
</span><span class="cx">     else
</span><span class="cx">         unregisterForTouchEvents();
</span><ins>+#endif
</ins><span class="cx"> }
</span><del>-#endif // ENABLE(IOS_TOUCH_EVENTS)
</del><span class="cx"> 
</span><span class="cx"> HTMLInputElement* SliderThumbElement::hostInput() const
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlshadowSliderThumbElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/shadow/SliderThumbElement.h (214954 => 214955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/shadow/SliderThumbElement.h        2017-04-05 18:43:33 UTC (rev 214954)
+++ trunk/Source/WebCore/html/shadow/SliderThumbElement.h        2017-04-05 18:47:39 UTC (rev 214955)
</span><span class="lines">@@ -52,9 +52,9 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(IOS_TOUCH_EVENTS)
</span><span class="cx">     void handleTouchEvent(TouchEvent&amp;);
</span><ins>+#endif
</ins><span class="cx"> 
</span><span class="cx">     void disabledAttributeChanged();
</span><del>-#endif
</del><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     SliderThumbElement(Document&amp;);
</span></span></pre>
</div>
</div>

</body>
</html>