<!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>[201832] 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/201832">201832</a></dd>
<dt>Author</dt> <dd>n_wang@apple.com</dd>
<dt>Date</dt> <dd>2016-06-08 14:15:09 -0700 (Wed, 08 Jun 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>For keyboard users, activating a fragment URL should transfer focus and caret to the destination
https://bugs.webkit.org/show_bug.cgi?id=116046

Reviewed by Ryosuke Niwa.

Source/WebCore:

Added a sequential focus navigation starting node to document. When TAB or SHIFT-TAB is pressed
and there is no focused element, we start searching for next focus candidates at the sequential
focus navigation node.
Spec: https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point

Test: fast/events/sequential-focus-navigation-starting-point.html

* dom/Document.cpp:
(WebCore::Document::removedLastRef):
(WebCore::Document::destroyRenderTree):
(WebCore::Document::styleResolverChanged):
(WebCore::isNodeInSubtree):
(WebCore::Document::removeFocusedNodeOfSubtree):
(WebCore::Document::hoveredElementDidDetach):
(WebCore::Document::setFocusedElement):
(WebCore::shouldResetFocusNavigationStartingNode):
(WebCore::Document::setFocusNavigationStartingNode):
(WebCore::Document::focusNavigationStartingNode):
(WebCore::Document::setCSSTarget):
(WebCore::Document::nodeChildrenWillBeRemoved):
(WebCore::Document::nodeWillBeRemoved):
(WebCore::fallbackFocusNavigationStartingNodeAfterRemoval):
(WebCore::Document::removeFocusNavigationNodeOfSubtree):
(WebCore::Document::textInserted):
* dom/Document.h:
(WebCore::Document::userActionElements):
* page/EventHandler.cpp:
(WebCore::EventHandler::handleMousePressEvent):
* page/FocusController.cpp:
(WebCore::FocusController::advanceFocusInDocumentOrder):
* page/FrameView.cpp:
(WebCore::FrameView::scrollToAnchor):

LayoutTests:

Added a layout test to check that mouse pressing, fragment navigation, focusing an element and removing
the focused element will give us the expected focus navigation starting point.

Also updated the fragment activation test because now that navigating to an unfocusable fragment will
unfocus the current focused element.

* fast/dom/fragment-activation-focuses-target-expected.txt:
* fast/dom/fragment-activation-focuses-target.html:
* fast/events/sequential-focus-navigation-starting-point-expected.txt: Added.
* fast/events/sequential-focus-navigation-starting-point.html: Added.
* platform/ios-simulator/TestExpectations:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastdomfragmentactivationfocusestargetexpectedtxt">trunk/LayoutTests/fast/dom/fragment-activation-focuses-target-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastdomfragmentactivationfocusestargethtml">trunk/LayoutTests/fast/dom/fragment-activation-focuses-target.html</a></li>
<li><a href="#trunkLayoutTestsplatformiossimulatorTestExpectations">trunk/LayoutTests/platform/ios-simulator/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCorepageEventHandlercpp">trunk/Source/WebCore/page/EventHandler.cpp</a></li>
<li><a href="#trunkSourceWebCorepageFocusControllercpp">trunk/Source/WebCore/page/FocusController.cpp</a></li>
<li><a href="#trunkSourceWebCorepageFrameViewcpp">trunk/Source/WebCore/page/FrameView.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfasteventssequentialfocusnavigationstartingpointexpectedtxt">trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfasteventssequentialfocusnavigationstartingpointhtml">trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/LayoutTests/ChangeLog        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -1,3 +1,22 @@
</span><ins>+2016-06-08  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        For keyboard users, activating a fragment URL should transfer focus and caret to the destination
+        https://bugs.webkit.org/show_bug.cgi?id=116046
+
+        Reviewed by Ryosuke Niwa.
+
+        Added a layout test to check that mouse pressing, fragment navigation, focusing an element and removing
+        the focused element will give us the expected focus navigation starting point.
+
+        Also updated the fragment activation test because now that navigating to an unfocusable fragment will
+        unfocus the current focused element.
+
+        * fast/dom/fragment-activation-focuses-target-expected.txt:
+        * fast/dom/fragment-activation-focuses-target.html:
+        * fast/events/sequential-focus-navigation-starting-point-expected.txt: Added.
+        * fast/events/sequential-focus-navigation-starting-point.html: Added.
+        * platform/ios-simulator/TestExpectations:
+
</ins><span class="cx"> 2016-06-07  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION (r201667): ASSERTION FAILED: !m_anchorNode || !editingIgnoresContent(*m_anchorNode)
</span></span></pre></div>
<a id="trunkLayoutTestsfastdomfragmentactivationfocusestargetexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/dom/fragment-activation-focuses-target-expected.txt (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/dom/fragment-activation-focuses-target-expected.txt        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/LayoutTests/fast/dom/fragment-activation-focuses-target-expected.txt        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -14,9 +14,9 @@
</span><span class="cx"> Verify Tab behaves correctly after following the link.
</span><span class="cx"> PASS document.activeElement is document.getElementById('fragment3')
</span><span class="cx"> PASS document.activeElement is document.getElementById('fragment1')
</span><del>-Activate a link that does not have a focusable fragment and verify focus does not move.
</del><ins>+Activate a link that does not have a focusable fragment and verify that the currently focused element is unfocused.
</ins><span class="cx"> PASS document.activeElement is link2
</span><del>-PASS document.activeElement is link2
</del><ins>+PASS document.activeElement is document.body
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestsfastdomfragmentactivationfocusestargethtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/dom/fragment-activation-focuses-target.html (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/dom/fragment-activation-focuses-target.html        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/LayoutTests/fast/dom/fragment-activation-focuses-target.html        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -43,12 +43,12 @@
</span><span class="cx">   shouldBe(&quot;document.activeElement&quot;, &quot;document.getElementById('fragment1')&quot;);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-debug(&quot;Activate a link that does not have a focusable fragment and verify focus does not move.&quot;);
</del><ins>+debug(&quot;Activate a link that does not have a focusable fragment and verify that the currently focused element is unfocused.&quot;);
</ins><span class="cx"> var link2 = document.getElementById(&quot;link2&quot;);
</span><span class="cx"> link2.focus();
</span><span class="cx"> shouldBe(&quot;document.activeElement&quot;, &quot;link2&quot;);
</span><span class="cx"> link2.click();
</span><del>-shouldBe(&quot;document.activeElement&quot;, &quot;link2&quot;);
</del><ins>+shouldBe(&quot;document.activeElement&quot;, &quot;document.body&quot;);
</ins><span class="cx"> 
</span><span class="cx"> var successfullyParsed = true;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsfasteventssequentialfocusnavigationstartingpointexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point-expected.txt (0 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point-expected.txt        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+Mouse press should update sequential focus navigation starting point.
+PASS container.innerHTML = '&lt;input id=prev&gt;&lt;div style=&quot;height:200px;&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusSpan(); moveFocus('forward'); document.activeElement.id is 'next'
+PASS container.innerHTML = '&lt;input id=prev&gt;&lt;div style=&quot;height:200px;&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusSpan(); moveFocus('backward'); document.activeElement.id is 'prev'
+PASS container.innerHTML = '&lt;span style=&quot;font-size:60px;&quot;&gt;&lt;input id=prev&gt;Text Text&lt;input id=next&gt;&lt;/span&gt;'; focusSpan(); moveFocus('forward'); document.activeElement.id is 'next'
+PASS container.innerHTML = '&lt;span style=&quot;font-size:60px;&quot;&gt;&lt;input id=prev&gt;Text Text&lt;input id=next&gt;&lt;/span&gt;'; focusSpan(); moveFocus('backward'); document.activeElement.id is 'prev'
+
+Fragment navigation should update sequential focus navigation starting point.
+PASS container.innerHTML = '&lt;a href=&quot;#fragment&quot;&gt;&lt;/a&gt;&lt;input id=prev&gt;&lt;a name=&quot;fragment&quot;&gt;&lt;/a&gt;&lt;input id=next&gt;'; clickLink(); moveFocus('forward'); document.activeElement.id is 'next'
+
+Focusing an element should update sequential focus navigation starting point.
+PASS container.innerHTML = '&lt;input id=prev&gt;&lt;input id=start&gt;&lt;input id=next&gt;'; focusStart(); moveFocus('forward'); document.activeElement.id is 'next'
+
+After removing a focused element from the document tree, sequential focus navigation should start at a place where the focused element was.
+PASS container.innerHTML = '&lt;input id=prev&gt;&lt;input id=start&gt;&lt;input id=next&gt;'; focusStart(); removeStart(); moveFocus('forward'); document.activeElement.id is 'next'
+PASS container.innerHTML = '&lt;input id=prev&gt;&lt;input id=start&gt;&lt;input id=next&gt;'; focusStart(); removeStart(); moveFocus('backward'); document.activeElement.id is 'prev'
+PASS container.innerHTML = '&lt;input id=prev&gt;&lt;div&gt;&lt;input id=start&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusStart(); removeDiv(); moveFocus('forward'); document.activeElement.id is 'next'
+PASS container.innerHTML = '&lt;div&gt;&lt;input id=start&gt;&lt;input id=prev&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusStart(); removeStart(); moveFocus('forward'); document.activeElement.id is 'prev'
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfasteventssequentialfocusnavigationstartingpointhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point.html (0 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point.html                                (rev 0)
+++ trunk/LayoutTests/fast/events/sequential-focus-navigation-starting-point.html        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -0,0 +1,69 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;body onload=&quot;runTest();&quot;&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../forms/resources/common.js&quot;&gt;&lt;/script&gt;
+&lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
+&lt;div id=&quot;container&quot;&gt;&lt;/div&gt;
+&lt;script&gt;
+if (!window.eventSender)
+    document.body.textContent = 'This test requires window.eventSender.';
+
+function focusSpan() { 
+    hoverOverElement(container.querySelector('span')); 
+    eventSender.mouseDown();
+}
+
+function focusDiv() { 
+    hoverOverElement(container.querySelector('div')); 
+    eventSender.mouseDown();
+}
+
+function moveFocus(direction) { 
+    eventSender.keyDown('\t', direction == 'forward' ? [] : ['shiftKey']); 
+}
+
+function clickLink() {
+    container.querySelector('a').click();
+}
+
+function focusStart() {
+    container.querySelector('#start').focus();
+}
+
+function removeStart() {
+    container.querySelector('#start').remove();
+}
+
+function removeDiv() {
+    container.querySelector('div').remove();
+}
+
+function runTest() {
+    
+    debug(&quot;Mouse press should update sequential focus navigation starting point.&quot;);
+    var container = document.querySelector('#container');
+    
+    shouldBe(&quot;container.innerHTML = '&lt;input id=prev&gt;&lt;div style=\&quot;height:200px;\&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusSpan(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'next'&quot;);
+    eventSender.mouseUp();
+    shouldBe(&quot;container.innerHTML = '&lt;input id=prev&gt;&lt;div style=\&quot;height:200px;\&quot;&gt;&lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusSpan(); moveFocus('backward'); document.activeElement.id&quot;, &quot;'prev'&quot;);
+    eventSender.mouseUp();
+    shouldBe(&quot;container.innerHTML = '&lt;span style=\&quot;font-size:60px;\&quot;&gt;&lt;input id=prev&gt;Text Text&lt;input id=next&gt;&lt;/span&gt;'; focusSpan(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'next'&quot;);
+    eventSender.mouseUp();
+    shouldBe(&quot;container.innerHTML = '&lt;span style=\&quot;font-size:60px;\&quot;&gt;&lt;input id=prev&gt;Text Text&lt;input id=next&gt;&lt;/span&gt;'; focusSpan(); moveFocus('backward'); document.activeElement.id&quot;, &quot;'prev'&quot;);
+    eventSender.mouseUp();
+    
+    debug(&quot;\nFragment navigation should update sequential focus navigation starting point.&quot;);
+    shouldBe(&quot;container.innerHTML = '&lt;a href=\&quot;#fragment\&quot;&gt;&lt;/a&gt;&lt;input id=prev&gt;&lt;a name=\&quot;fragment\&quot;&gt;&lt;/a&gt;&lt;input id=next&gt;'; clickLink(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'next'&quot;);
+    
+    debug(&quot;\nFocusing an element should update sequential focus navigation starting point.&quot;);
+    shouldBe(&quot;container.innerHTML = '&lt;input id=prev&gt;&lt;input id=start&gt;&lt;input id=next&gt;'; focusStart(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'next'&quot;);
+    
+    debug(&quot;\nAfter removing a focused element from the document tree, sequential focus navigation should start at a place where the focused element was.&quot;);
+    shouldBe(&quot;container.innerHTML = '&lt;input id=prev&gt;&lt;input id=start&gt;&lt;input id=next&gt;'; focusStart(); removeStart(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'next'&quot;);
+    shouldBe(&quot;container.innerHTML = '&lt;input id=prev&gt;&lt;input id=start&gt;&lt;input id=next&gt;'; focusStart(); removeStart(); moveFocus('backward'); document.activeElement.id&quot;, &quot;'prev'&quot;);
+    shouldBe(&quot;container.innerHTML = '&lt;input id=prev&gt;&lt;div&gt;&lt;input id=start&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusStart(); removeDiv(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'next'&quot;);
+    shouldBe(&quot;container.innerHTML = '&lt;div&gt;&lt;input id=start&gt;&lt;input id=prev&gt;&lt;/div&gt;&lt;input id=next&gt;'; focusStart(); removeStart(); moveFocus('forward'); document.activeElement.id&quot;, &quot;'prev'&quot;);
+}
+
+&lt;/script&gt;
+&lt;/body&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkLayoutTestsplatformiossimulatorTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-simulator/TestExpectations        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -263,6 +263,7 @@
</span><span class="cx"> fast/shadow-dom/focus-navigation-across-slots.html [ Failure ]
</span><span class="cx"> fast/shadow-dom/focus-on-iframe.html [ Failure ]
</span><span class="cx"> fast/shadow-dom/negative-tabindex-on-shadow-host.html [ Failure ]
</span><ins>+webkit.org/b/116046 fast/events/sequential-focus-navigation-starting-point.html [ Skip ]
</ins><span class="cx"> 
</span><span class="cx"> webkit.org/b/150225 fast/custom-elements [ Pass ]
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/Source/WebCore/ChangeLog        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -1,3 +1,43 @@
</span><ins>+2016-06-08  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        For keyboard users, activating a fragment URL should transfer focus and caret to the destination
+        https://bugs.webkit.org/show_bug.cgi?id=116046
+
+        Reviewed by Ryosuke Niwa.
+
+        Added a sequential focus navigation starting node to document. When TAB or SHIFT-TAB is pressed
+        and there is no focused element, we start searching for next focus candidates at the sequential
+        focus navigation node.
+        Spec: https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point
+
+        Test: fast/events/sequential-focus-navigation-starting-point.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::removedLastRef):
+        (WebCore::Document::destroyRenderTree):
+        (WebCore::Document::styleResolverChanged):
+        (WebCore::isNodeInSubtree):
+        (WebCore::Document::removeFocusedNodeOfSubtree):
+        (WebCore::Document::hoveredElementDidDetach):
+        (WebCore::Document::setFocusedElement):
+        (WebCore::shouldResetFocusNavigationStartingNode):
+        (WebCore::Document::setFocusNavigationStartingNode):
+        (WebCore::Document::focusNavigationStartingNode):
+        (WebCore::Document::setCSSTarget):
+        (WebCore::Document::nodeChildrenWillBeRemoved):
+        (WebCore::Document::nodeWillBeRemoved):
+        (WebCore::fallbackFocusNavigationStartingNodeAfterRemoval):
+        (WebCore::Document::removeFocusNavigationNodeOfSubtree):
+        (WebCore::Document::textInserted):
+        * dom/Document.h:
+        (WebCore::Document::userActionElements):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::handleMousePressEvent):
+        * page/FocusController.cpp:
+        (WebCore::FocusController::advanceFocusInDocumentOrder):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::scrollToAnchor):
+
</ins><span class="cx"> 2016-06-08  Eric Carlson  &lt;eric.carlson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         HTMLMediaElement.prototype.canPlayType accounting for 250-750ms first loading theverge.com
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/Source/WebCore/dom/Document.cpp        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -680,6 +680,7 @@
</span><span class="cx">         m_activeElement = nullptr;
</span><span class="cx">         m_titleElement = nullptr;
</span><span class="cx">         m_documentElement = nullptr;
</span><ins>+        m_focusNavigationStartingNode = nullptr;
</ins><span class="cx">         m_userActionElements.documentDidRemoveLastRef();
</span><span class="cx"> #if ENABLE(FULLSCREEN_API)
</span><span class="cx">         m_fullScreenElement = nullptr;
</span><span class="lines">@@ -2308,6 +2309,7 @@
</span><span class="cx">     m_hoveredElement = nullptr;
</span><span class="cx">     m_focusedElement = nullptr;
</span><span class="cx">     m_activeElement = nullptr;
</span><ins>+    m_focusNavigationStartingNode = nullptr;
</ins><span class="cx"> 
</span><span class="cx">     if (m_documentElement)
</span><span class="cx">         RenderTreeUpdater::tearDownRenderers(*m_documentElement);
</span><span class="lines">@@ -3693,6 +3695,17 @@
</span><span class="cx">     evaluateMediaQueryList();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool isNodeInSubtree(Node* node, Node* container, bool amongChildrenOnly)
+{
+    bool nodeInSubtree = false;
+    if (amongChildrenOnly)
+        nodeInSubtree = node-&gt;isDescendantOf(container);
+    else
+        nodeInSubtree = (node == container) || node-&gt;isDescendantOf(container);
+    
+    return nodeInSubtree;
+}
+
</ins><span class="cx"> void Document::removeFocusedNodeOfSubtree(Node* node, bool amongChildrenOnly)
</span><span class="cx"> {
</span><span class="cx">     if (!m_focusedElement || this-&gt;inPageCache()) // If the document is in the page cache, then we don't need to clear out the focused node.
</span><span class="lines">@@ -3701,15 +3714,15 @@
</span><span class="cx">     Element* focusedElement = node-&gt;treeScope().focusedElement();
</span><span class="cx">     if (!focusedElement)
</span><span class="cx">         return;
</span><del>-
-    bool nodeInSubtree = false;
-    if (amongChildrenOnly)
-        nodeInSubtree = focusedElement-&gt;isDescendantOf(node);
-    else
-        nodeInSubtree = (focusedElement == node) || focusedElement-&gt;isDescendantOf(node);
</del><span class="cx">     
</span><del>-    if (nodeInSubtree)
</del><ins>+    if (isNodeInSubtree(focusedElement, node, amongChildrenOnly)) {
</ins><span class="cx">         setFocusedElement(nullptr, FocusDirectionNone, FocusRemovalEventsMode::DoNotDispatch);
</span><ins>+        // Set the focus navigation starting node to the previous focused element so that
+        // we can fallback to the siblings or parent node for the next search.
+        // Also we need to call removeFocusNavigationNodeOfSubtree after this function because
+        // setFocusedElement(nullptr) will reset m_focusNavigationStartingNode.
+        setFocusNavigationStartingNode(focusedElement);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void Document::hoveredElementDidDetach(Element* element)
</span><span class="lines">@@ -3769,6 +3782,7 @@
</span><span class="cx">             oldFocusedElement-&gt;setActive(false);
</span><span class="cx"> 
</span><span class="cx">         oldFocusedElement-&gt;setFocus(false);
</span><ins>+        setFocusNavigationStartingNode(nullptr);
</ins><span class="cx"> 
</span><span class="cx">         if (eventsMode == FocusRemovalEventsMode::Dispatch) {
</span><span class="cx">             // Dispatch a change event for form control elements that have been edited.
</span><span class="lines">@@ -3819,6 +3833,7 @@
</span><span class="cx">         }
</span><span class="cx">         // Set focus on the new node
</span><span class="cx">         m_focusedElement = newFocusedElement;
</span><ins>+        setFocusNavigationStartingNode(m_focusedElement.get());
</ins><span class="cx"> 
</span><span class="cx">         // Dispatch the focus event and let the node do any other focus related activities (important for text fields)
</span><span class="cx">         m_focusedElement-&gt;dispatchFocusEvent(oldFocusedElement.copyRef(), direction);
</span><span class="lines">@@ -3885,6 +3900,59 @@
</span><span class="cx">     return !focusChangeBlocked;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool shouldResetFocusNavigationStartingNode(Node&amp; node)
+{
+    // Setting focus navigation starting node to the following nodes means that we should start
+    // the search from the beginning of the document.
+    return is&lt;HTMLHtmlElement&gt;(node) || is&lt;HTMLDocument&gt;(node);
+}
+
+void Document::setFocusNavigationStartingNode(Node* node)
+{
+    if (!m_frame)
+        return;
+
+    m_focusNavigationStartingNodeIsRemoved = false;
+    if (!node || shouldResetFocusNavigationStartingNode(*node)) {
+        m_focusNavigationStartingNode = nullptr;
+        return;
+    }
+
+    m_focusNavigationStartingNode = node;
+}
+
+Element* Document::focusNavigationStartingNode(FocusDirection direction) const
+{
+    if (m_focusedElement) {
+        if (!m_focusNavigationStartingNode || !m_focusNavigationStartingNode-&gt;isDescendantOf(m_focusedElement.get()))
+            return m_focusedElement.get();
+    }
+
+    if (!m_focusNavigationStartingNode)
+        return nullptr;
+
+    Node* node = m_focusNavigationStartingNode.get();
+    
+    // When the node was removed from the document tree. This case is not specified in the spec:
+    // https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point
+    // Current behaivor is to move the sequential navigation node to / after (based on the focus direction)
+    // the previous sibling of the removed node.
+    if (m_focusNavigationStartingNodeIsRemoved) {
+        Node* nextNode = NodeTraversal::next(*node);
+        if (direction == FocusDirectionForward)
+            return ElementTraversal::previous(*nextNode);
+        if (is&lt;Element&gt;(*nextNode))
+            return downcast&lt;Element&gt;(nextNode);
+        return ElementTraversal::next(*nextNode);
+    }
+
+    if (is&lt;Element&gt;(*node))
+        return downcast&lt;Element&gt;(node);
+    if (Element* elementBeforeNextFocusableElement = direction == FocusDirectionForward ? ElementTraversal::previous(*node) : ElementTraversal::next(*node))
+        return elementBeforeNextFocusableElement;
+    return node-&gt;parentOrShadowHostElement();
+}
+
</ins><span class="cx"> void Document::setCSSTarget(Element* n)
</span><span class="cx"> {
</span><span class="cx">     if (m_cssTarget)
</span><span class="lines">@@ -3982,6 +4050,7 @@
</span><span class="cx">     NoEventDispatchAssertion assertNoEventDispatch;
</span><span class="cx"> 
</span><span class="cx">     removeFocusedNodeOfSubtree(&amp;container, true /* amongChildrenOnly */);
</span><ins>+    removeFocusNavigationNodeOfSubtree(container, true /* amongChildrenOnly */);
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(FULLSCREEN_API)
</span><span class="cx">     removeFullScreenElementOfSubtree(&amp;container, true /* amongChildrenOnly */);
</span><span class="lines">@@ -4014,6 +4083,7 @@
</span><span class="cx">     NoEventDispatchAssertion assertNoEventDispatch;
</span><span class="cx"> 
</span><span class="cx">     removeFocusedNodeOfSubtree(&amp;n);
</span><ins>+    removeFocusNavigationNodeOfSubtree(n);
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(FULLSCREEN_API)
</span><span class="cx">     removeFullScreenElementOfSubtree(&amp;n);
</span><span class="lines">@@ -4035,6 +4105,22 @@
</span><span class="cx">         m_markers-&gt;removeMarkers(&amp;n);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static Node* fallbackFocusNavigationStartingNodeAfterRemoval(Node&amp; node)
+{
+    return node.previousSibling() ? node.previousSibling() : node.parentNode();
+}
+
+void Document::removeFocusNavigationNodeOfSubtree(Node&amp; node, bool amongChildrenOnly)
+{
+    if (!m_focusNavigationStartingNode)
+        return;
+
+    if (isNodeInSubtree(m_focusNavigationStartingNode.get(), &amp;node, amongChildrenOnly)) {
+        m_focusNavigationStartingNode = amongChildrenOnly ? &amp;node : fallbackFocusNavigationStartingNodeAfterRemoval(node);
+        m_focusNavigationStartingNodeIsRemoved = true;
+    }
+}
+
</ins><span class="cx"> void Document::textInserted(Node* text, unsigned offset, unsigned length)
</span><span class="cx"> {
</span><span class="cx">     if (!m_ranges.isEmpty()) {
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/Source/WebCore/dom/Document.h        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -730,6 +730,9 @@
</span><span class="cx">     UserActionElementSet&amp; userActionElements()  { return m_userActionElements; }
</span><span class="cx">     const UserActionElementSet&amp; userActionElements() const { return m_userActionElements; }
</span><span class="cx"> 
</span><ins>+    void setFocusNavigationStartingNode(Node*);
+    Element* focusNavigationStartingNode(FocusDirection) const;
+
</ins><span class="cx">     void removeFocusedNodeOfSubtree(Node*, bool amongChildrenOnly = false);
</span><span class="cx">     void hoveredElementDidDetach(Element*);
</span><span class="cx">     void elementInActiveChainDidDetach(Element*);
</span><span class="lines">@@ -769,6 +772,7 @@
</span><span class="cx">     void nodeChildrenWillBeRemoved(ContainerNode&amp;);
</span><span class="cx">     // nodeWillBeRemoved is only safe when removing one node at a time.
</span><span class="cx">     void nodeWillBeRemoved(Node&amp;);
</span><ins>+    void removeFocusNavigationNodeOfSubtree(Node&amp;, bool amongChildrenOnly = false);
</ins><span class="cx">     enum class AcceptChildOperation { Replace, InsertOrAdd };
</span><span class="cx">     bool canAcceptChild(const Node&amp; newChild, const Node* refChild, AcceptChildOperation) const;
</span><span class="cx"> 
</span><span class="lines">@@ -1465,6 +1469,8 @@
</span><span class="cx"> 
</span><span class="cx">     Color m_textColor;
</span><span class="cx"> 
</span><ins>+    bool m_focusNavigationStartingNodeIsRemoved;
+    RefPtr&lt;Node&gt; m_focusNavigationStartingNode;
</ins><span class="cx">     RefPtr&lt;Element&gt; m_focusedElement;
</span><span class="cx">     RefPtr&lt;Element&gt; m_hoveredElement;
</span><span class="cx">     RefPtr&lt;Element&gt; m_activeElement;
</span></span></pre></div>
<a id="trunkSourceWebCorepageEventHandlercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/EventHandler.cpp (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/EventHandler.cpp        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/Source/WebCore/page/EventHandler.cpp        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -795,6 +795,7 @@
</span><span class="cx">         focusDocumentView();
</span><span class="cx"> 
</span><span class="cx">     m_mousePressNode = event.targetNode();
</span><ins>+    m_frame.document()-&gt;setFocusNavigationStartingNode(event.targetNode());
</ins><span class="cx"> #if ENABLE(DRAG_SUPPORT)
</span><span class="cx">     m_dragStartPos = event.event().position();
</span><span class="cx"> #endif
</span><span class="lines">@@ -1654,6 +1655,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     m_mousePressNode = mouseEvent.targetNode();
</span><ins>+    m_frame.document()-&gt;setFocusNavigationStartingNode(mouseEvent.targetNode());
</ins><span class="cx"> 
</span><span class="cx">     RefPtr&lt;Frame&gt; subframe = subframeForHitTestResult(mouseEvent);
</span><span class="cx">     if (subframe &amp;&amp; passMousePressEventToSubframe(mouseEvent, subframe.get())) {
</span></span></pre></div>
<a id="trunkSourceWebCorepageFocusControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/FocusController.cpp (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/FocusController.cpp        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/Source/WebCore/page/FocusController.cpp        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -454,7 +454,7 @@
</span><span class="cx">     Frame&amp; frame = focusedOrMainFrame();
</span><span class="cx">     Document* document = frame.document();
</span><span class="cx"> 
</span><del>-    Node* currentNode = document-&gt;focusedElement();
</del><ins>+    Node* currentNode = document-&gt;focusNavigationStartingNode(direction);
</ins><span class="cx">     // FIXME: Not quite correct when it comes to focus transitions leaving/entering the WebView itself
</span><span class="cx">     bool caretBrowsing = frame.settings().caretBrowsingEnabled();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorepageFrameViewcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/FrameView.cpp (201831 => 201832)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/FrameView.cpp        2016-06-08 21:07:42 UTC (rev 201831)
+++ trunk/Source/WebCore/page/FrameView.cpp        2016-06-08 21:15:09 UTC (rev 201832)
</span><span class="lines">@@ -2100,8 +2100,14 @@
</span><span class="cx">     maintainScrollPositionAtAnchor(scrollPositionAnchor);
</span><span class="cx">     
</span><span class="cx">     // If the anchor accepts keyboard focus, move focus there to aid users relying on keyboard navigation.
</span><del>-    if (anchorElement &amp;&amp; anchorElement-&gt;isFocusable())
-        document.setFocusedElement(anchorElement);
</del><ins>+    if (anchorElement) {
+        if (anchorElement-&gt;isFocusable())
+            document.setFocusedElement(anchorElement);
+        else {
+            document.setFocusedElement(nullptr);
+            document.setFocusNavigationStartingNode(anchorElement);
+        }
+    }
</ins><span class="cx">     
</span><span class="cx">     return true;
</span><span class="cx"> }
</span></span></pre>
</div>
</div>

</body>
</html>