<!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>[200520] 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/200520">200520</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-05-06 14:01:32 -0700 (Fri, 06 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>FKA: No way to get focus from DOM to shadow DOM components (Was: HTML5 media controls not keyboard accessible)
https://bugs.webkit.org/show_bug.cgi?id=117857

Reviewed by Jer Noble.

Source/WebCore:

The bug was caused by hasCustomFocusLogic returning true on media elements.

Fix the bug by removing this function so that FocusController will walk into the shadow tree of media elements
to look for focusable elements. This will allow AT such as Voice Over to iterate through controls.

We don't seem to draw focus rings inside the media elements but that could be tweaked in a separate patch.

Test: media/tab-focus-inside-media-elements.html

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::hasCustomFocusLogic): Deleted.
* html/HTMLMediaElement.h:

LayoutTests:

Added a regression test for moving focus into media elements by pressing tab key.

* media/tab-focus-inside-media-elements-expected.txt: Added.
* media/tab-focus-inside-media-elements.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementcpp">trunk/Source/WebCore/html/HTMLMediaElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementh">trunk/Source/WebCore/html/HTMLMediaElement.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsmediatabfocusinsidemediaelementsexpectedtxt">trunk/LayoutTests/media/tab-focus-inside-media-elements-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediatabfocusinsidemediaelementshtml">trunk/LayoutTests/media/tab-focus-inside-media-elements.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (200519 => 200520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-05-06 20:38:55 UTC (rev 200519)
+++ trunk/LayoutTests/ChangeLog        2016-05-06 21:01:32 UTC (rev 200520)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2016-05-06  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        FKA: No way to get focus from DOM to shadow DOM components (Was: HTML5 media controls not keyboard accessible)
+        https://bugs.webkit.org/show_bug.cgi?id=117857
+
+        Reviewed by Jer Noble.
+
+        Added a regression test for moving focus into media elements by pressing tab key.
+
+        * media/tab-focus-inside-media-elements-expected.txt: Added.
+        * media/tab-focus-inside-media-elements.html: Added.
+
</ins><span class="cx"> 2016-05-06  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         JS Function removed after parsing
</span></span></pre></div>
<a id="trunkLayoutTestsmediatabfocusinsidemediaelementsexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/tab-focus-inside-media-elements-expected.txt (0 => 200520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/tab-focus-inside-media-elements-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/tab-focus-inside-media-elements-expected.txt        2016-05-06 21:01:32 UTC (rev 200520)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+
+
+Tests for moving the focus onto controls inside an audio element and a video element.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS document.body.focus(); eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[0]
+PASS mediaElements[0] instanceof HTMLAudioElement is true
+PASS mediaElements[0].controls is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[0]
+PASS !!internals.shadowRoot(mediaElements[0]).activeElement /* play button */ is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[0]
+PASS !!internals.shadowRoot(mediaElements[0]).activeElement /* rewind button */ is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[0]
+PASS !!internals.shadowRoot(mediaElements[0]).activeElement /* volume slider */ is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[0]
+PASS !!internals.shadowRoot(mediaElements[0]).activeElement /* mute button */ is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[1]
+PASS mediaElements[1] instanceof HTMLVideoElement is true
+PASS mediaElements[1].controls is true
+FAIL !!internals.shadowRoot(mediaElements[1]).activeElement /* play button */ should be true. Was false.
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[1]
+PASS !!internals.shadowRoot(mediaElements[1]).activeElement /* rewind button */ is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[1]
+PASS !!internals.shadowRoot(mediaElements[1]).activeElement /* volume slider */ is true
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[1]
+PASS !!internals.shadowRoot(mediaElements[1]).activeElement /* mute button */ is true
+FAIL eventSender.keyDown(&quot;\t&quot;); document.activeElement should be [object HTMLAudioElement]. Was [object HTMLVideoElement].
+PASS mediaElements[2] instanceof HTMLAudioElement is true
+PASS mediaElements[2].controls is false
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is mediaElements[3]
+PASS mediaElements[3] instanceof HTMLVideoElement is true
+PASS mediaElements[3].controls is false
+PASS eventSender.keyDown(&quot;\t&quot;); document.activeElement is document.querySelector(&quot;div&quot;)
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediatabfocusinsidemediaelementshtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/tab-focus-inside-media-elements.html (0 => 200520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/tab-focus-inside-media-elements.html                                (rev 0)
+++ trunk/LayoutTests/media/tab-focus-inside-media-elements.html        2016-05-06 21:01:32 UTC (rev 200520)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+&lt;!DOCTYPE html!&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;audio controls&gt;&lt;/audio&gt;&lt;video controls&gt;&lt;/video&gt;&lt;br&gt;
+&lt;audio tabindex=&quot;0&quot;&gt;&lt;/audio&gt;&lt;video tabindex=&quot;0&quot;&gt;&lt;/video&gt;
+&lt;div tabindex=&quot;0&quot;&gt;&lt;/div&gt;
+&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
+&lt;script src=&quot;../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+
+description('Tests for moving the focus onto controls inside an audio element and a video element.');
+
+var mediaElements = document.querySelectorAll(&quot;audio,video&quot;);
+
+if (window.testRunner)
+    runTests();
+else
+    log('This test requires eventSender');
+
+function runTests()
+{
+    testRunner.overridePreference(&quot;WebKitTabToLinksPreferenceKey&quot;, 1);
+
+    shouldBe('document.body.focus(); eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[0]');
+    shouldBeTrue('mediaElements[0] instanceof HTMLAudioElement');
+    shouldBeTrue('mediaElements[0].controls');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[0]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[0]).activeElement /* play button */');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[0]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[0]).activeElement /* rewind button */');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[0]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[0]).activeElement /* volume slider */');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[0]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[0]).activeElement /* mute button */');
+
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[1]');
+    shouldBeTrue('mediaElements[1] instanceof HTMLVideoElement');
+    shouldBeTrue('mediaElements[1].controls');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[1]).activeElement /* play button */');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[1]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[1]).activeElement /* rewind button */');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[1]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[1]).activeElement /* volume slider */');
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[1]');
+    shouldBeTrue('!!internals.shadowRoot(mediaElements[1]).activeElement /* mute button */');
+
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[2]');
+    shouldBeTrue('mediaElements[2] instanceof HTMLAudioElement');
+    shouldBeFalse('mediaElements[2].controls');
+
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'mediaElements[3]');
+    shouldBeTrue('mediaElements[3] instanceof HTMLVideoElement');
+    shouldBeFalse('mediaElements[3].controls');
+
+    shouldBe('eventSender.keyDown(&quot;\\t&quot;); document.activeElement', 'document.querySelector(&quot;div&quot;)');
+}
+
+&lt;/script&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="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (200519 => 200520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-05-06 20:38:55 UTC (rev 200519)
+++ trunk/Source/WebCore/ChangeLog        2016-05-06 21:01:32 UTC (rev 200520)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2016-05-06  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        FKA: No way to get focus from DOM to shadow DOM components (Was: HTML5 media controls not keyboard accessible)
+        https://bugs.webkit.org/show_bug.cgi?id=117857
+
+        Reviewed by Jer Noble.
+
+        The bug was caused by hasCustomFocusLogic returning true on media elements.
+
+        Fix the bug by removing this function so that FocusController will walk into the shadow tree of media elements
+        to look for focusable elements. This will allow AT such as Voice Over to iterate through controls.
+
+        We don't seem to draw focus rings inside the media elements but that could be tweaked in a separate patch.
+
+        Test: media/tab-focus-inside-media-elements.html
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::hasCustomFocusLogic): Deleted.
+        * html/HTMLMediaElement.h:
+
</ins><span class="cx"> 2016-05-06  Anders Carlsson  &lt;andersca@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Tidy up the LinkRelAttribute code
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (200519 => 200520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-05-06 20:38:55 UTC (rev 200519)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-05-06 21:01:32 UTC (rev 200520)
</span><span class="lines">@@ -659,11 +659,6 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-bool HTMLMediaElement::hasCustomFocusLogic() const
-{
-    return true;
-}
-
</del><span class="cx"> bool HTMLMediaElement::supportsFocus() const
</span><span class="cx"> {
</span><span class="cx">     if (document().isMediaDocument())
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (200519 => 200520)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.h        2016-05-06 20:38:55 UTC (rev 200519)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h        2016-05-06 21:01:32 UTC (rev 200520)
</span><span class="lines">@@ -496,7 +496,6 @@
</span><span class="cx">     // FIXME: Shadow DOM spec says we should be able to create shadow root on audio and video elements
</span><span class="cx">     bool canHaveUserAgentShadowRoot() const final { return true; }
</span><span class="cx"> 
</span><del>-    bool hasCustomFocusLogic() const override;
</del><span class="cx">     bool supportsFocus() const override;
</span><span class="cx">     bool isMouseFocusable() const override;
</span><span class="cx">     bool rendererIsNeeded(const RenderStyle&amp;) override;
</span></span></pre>
</div>
</div>

</body>
</html>