[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-9427-gc2be6fc

yael.aharon at nokia.com yael.aharon at nokia.com
Wed Dec 22 16:20:33 UTC 2010


The following commit has been merged in the debian/experimental branch:
commit 0a818c213126ad3cc4c507d3765e5bd8870a8355
Author: yael.aharon at nokia.com <yael.aharon at nokia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Mon Nov 22 13:49:09 2010 +0000

    Spatial Navigation: issues with the node selection algorithm.
    https://bugs.webkit.org/show_bug.cgi?id=49382
    
    Patch by Yael Aharon  <yael.aharon at nokia.com>, Chang Shu <chang.shu at nokia.com> on 2010-11-22
    Reviewed by Antonio Gomes.
    
    WebCore:
    
    Modify the Spatial Navigation algorithm, to better handle initial focus and
    navigation between frames.
    The new algorithm takes the rect of the focused node as the startingRect,
    instead of the node itself. That allows us to construct a virtual rect if
    there is no focused node, or if it is off the screen.
    The virtual rect is the edge of the container in the direction of the navigation.
    
    With this patch, scrollable containers and frames will scroll regardless of weather
    they have focusable content. Users will be able to use arrow keys to view all the
    content of such a container. The only exception is if the container has style overflow:hidden.
    We will not scroll in that case.
    
    With this patch, we handle z-index and positioning so that if there are 2 overlapping focusable nodes,
    we do a hit test and only the node on top can get focus.
    
    hasOffScreenRect() was modified so that it can check if a node will be off-screen even after we scrolled
    its parent container. We do not add the scrolling conditions for containers that have overflow:hidden
    and cannot scroll.
    
    calculateScrollbarModesForLayout is used to decide if a frame can scroll or not. We cannot rely on
    the exsistance of the scrollbar, because it could be removed via the API, while the frame is still
    allowed to scroll.
    
    * page/FocusController.cpp:
    (WebCore::updateFocusCandidateIfNeeded):
    (WebCore::FocusController::findFocusCandidateInContainer):
    (WebCore::FocusController::advanceFocusDirectionallyInContainer):
    (WebCore::FocusController::advanceFocusDirectionally):
    * page/FocusController.h:
    * page/FrameView.h:
    * page/SpatialNavigation.cpp:
    (WebCore::FocusCandidate::FocusCandidate):
    (WebCore::distanceDataForNode):
    (WebCore::alignmentForRects):
    (WebCore::areRectsMoreThanFullScreenApart):
    (WebCore::isRectInDirection):
    (WebCore::hasOffscreenRect):
    (WebCore::scrollInDirection):
    (WebCore::isScrollableContainerNode):
    (WebCore::scrollableEnclosingBoxOrParentFrameForNodeInDirection):
    (WebCore::canScrollInDirection):
    (WebCore::rectToAbsoluteCoordinates):
    (WebCore::nodeRectInAbsoluteCoordinates):
    (WebCore::frameRectInAbsoluteCoordinates):
    (WebCore::entryAndExitPointsForDirection):
    (WebCore::canBeScrolledIntoView):
    (WebCore::virtualRectForDirection):
    * page/SpatialNavigation.h:
    
    LayoutTests:
    
    Replaced text in tests with images with fixed size to make them more cross platform and modified
    test results to reflect that we can scroll container that do not include focusable content.
    
    * fast/events/spatial-navigation/snav-clipped-overflowed-content-expected.txt:
    * fast/events/spatial-navigation/snav-clipped-overflowed-content.html:
    * fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt:
    * fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html:
    * fast/events/spatial-navigation/snav-iframe-no-focusable-content-expected.txt:
    * fast/events/spatial-navigation/snav-iframe-no-focusable-content.html:
    * fast/events/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt:
    * fast/events/spatial-navigation/snav-iframe-no-scrollable-content.html:
    * fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt:
    * fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html:
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@72522 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index d9e72ce..dadcbfb 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,24 @@
+2010-11-22  Yael Aharon  <yael.aharon at nokia.com>, Chang Shu  <chang.shu at nokia.com>
+
+        Reviewed by Antonio Gomes.
+
+        Spatial Navigation: issues with the node selection algorithm.
+        https://bugs.webkit.org/show_bug.cgi?id=49382
+
+        Replaced text in tests with images with fixed size to make them more cross platform and modified
+        test results to reflect that we can scroll container that do not include focusable content. 
+
+        * fast/events/spatial-navigation/snav-clipped-overflowed-content-expected.txt:
+        * fast/events/spatial-navigation/snav-clipped-overflowed-content.html:
+        * fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt:
+        * fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html:
+        * fast/events/spatial-navigation/snav-iframe-no-focusable-content-expected.txt:
+        * fast/events/spatial-navigation/snav-iframe-no-focusable-content.html:
+        * fast/events/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt:
+        * fast/events/spatial-navigation/snav-iframe-no-scrollable-content.html:
+        * fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt:
+        * fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html:
+
 2010-11-22  Anton Muhin  <antonm at chromium.org>
 
         Amending suppressions from previous commit.
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content-expected.txt b/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content-expected.txt
index cf93b4f..6a82226 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content-expected.txt
+++ b/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content-expected.txt
@@ -6,9 +6,13 @@
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
 This test tests that a div with css overflow:auto will scroll to reveal its focusable elements
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content.html b/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content.html
index 55b69b5..e8b7cca 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content.html
+++ b/LayoutTests/fast/events/spatial-navigation/snav-clipped-overflowed-content.html
@@ -36,9 +36,13 @@
       ["Down", "1"],
       ["Down", "1"],
       ["Down", "2"],
+      ["Down", "2"],
+      ["Down", "2"],
       ["Down", "3"],
       ["Up", "2"],
       ["Up", "2"],
+      ["Up", "2"],
+      ["Up", "1"],
       ["Up", "1"],
       ["Up", "start"],
       ["DONE", "DONE"]
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt b/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt
index 281e6c5..4816fbb 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt
+++ b/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt
@@ -1,35 +1,19 @@
-a
-This is a scrollable Div created with the CSS property overflow.
 
-It has no keyboard focusable elements ....
 
 
 
-
-
-
-
-
-
-
-
-
-
-... and scrollbars!
-
-This is another scrollable div created with the CSS property overflow.
-
-It has also no keyboard focusable elements ....
-
-
-
-
-
-
-
-... as well as scrollbars!
-
-e
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-
+This test is to test that a scrollable div can scroll and reveal its content even if it does not have any focusable content.
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html b/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html
index ba702f7..8a40c83 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html
+++ b/LayoutTests/fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html
@@ -28,7 +28,19 @@
     <script type="application/javascript">
 
     var resultMap = [
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
       ["Down", "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
       ["Up"  , "start"],
       ["DONE", "DONE"]
     ];
@@ -59,22 +71,16 @@
     <script src="../js/resources/js-test-post.js"></script>
   </head>
   <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-    <div><a id="start" href="a">a</a></div>
+    <div><a id="start" href="a"><img src="resources/green.png" width=30 height=30></a></div>
     <div class="scroll">
-      <p>This is a scrollable Div created with the CSS property overflow.</p>
-      <p>It has no keyboard focusable elements ....</p>
-      <br><br><br><br><br><br>
-      <br><br><br><br><br><br>
-      <p>... and scrollbars!</p>
+      <img src="resources/green.png" width=240 height=300>
     </div>
 
     <div class="scroll">
-      <p>This is another scrollable div created with the CSS property overflow.</p>
-      <p>It has also no keyboard focusable elements ....</p>
-      <br><br><br><br><br><br>
-      <p>... as well as scrollbars!</p>
+      <img src="resources/green.png" width=240 height=300>
     </div>
-    <div><a id="end" href="a">e</a></div>
+    <div><a id="end" href="a"><img src="resources/green.png" width=30 height=30></a></div>
     <div id="console"></div>
+    This test is to test that a scrollable div can scroll and reveal its content even if it does not have any focusable content.
   </body>
 </html>
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content-expected.txt b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content-expected.txt
index cd80e51..47330cb 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content-expected.txt
+++ b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content-expected.txt
@@ -2,6 +2,18 @@ a
 
 
 e
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
-
+This is to test that an iframe with no focusable content still scrolls
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content.html b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content.html
index 4d7d4e2..3f59fca 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content.html
+++ b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-focusable-content.html
@@ -17,7 +17,19 @@
     <script type="application/javascript">
 
     var resultMap = [
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
+      ["Down", "start"],
       ["Down", "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
+      ["Up"  , "end"],
       ["Up"  , "start"],
       ["DONE", "DONE"]
     ];
@@ -52,23 +64,18 @@
 
     <iframe width="80" height="80" scrolling="auto" src="data:text/html,
       <body>
-        <div style='margin-top:120px'>
-          <p>This frame has some text and no focusable element ...</a>
-        </div>
-        <p>... and some more text ...</a>
+        <img width=120 height=200 src='resources/green.png'>
       </body>
     "></iframe><br>
 
     <iframe scrolling="auto" src="data:text/html,
       <body>
-        <div style='margin-top:120px'>
-          <p>This frame has some text and no focusable element ...</a>
-        </div>
-        <p>... and some more text ...</a>
+        <img width=120 height=200 src='resources/green.png'>
       </body>
     "></iframe><br>
 
     <a id="end" href="a">e</a>
     <div id="console"></div>
+    This is to test that an iframe with no focusable content still scrolls
   </body>
 </html>
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt
index 212a8af..ad1fb73 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt
+++ b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt
@@ -11,6 +11,7 @@ PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "4"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "5"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "6"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "7"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "3"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content.html b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content.html
index 55c0566..206039f 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content.html
+++ b/LayoutTests/fast/events/spatial-navigation/snav-iframe-no-scrollable-content.html
@@ -27,6 +27,7 @@
       ["Up", "4"],
       ["Right", "5"],
       ["Right", "6"],
+      ["Up", "3"],
       ["Right", "7"],
       ["Up", "3"],
       ["Up", "2"],
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt b/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt
index eda68ef..b03025a 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt
+++ b/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt
@@ -1,6 +1,9 @@
-a
 
-e
+
+PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
@@ -9,6 +12,10 @@ PASS gFocusedDocument.activeElement.getAttribute("id") is "end"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
+PASS gFocusedDocument.activeElement.getAttribute("id") is "2"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
 PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
 
diff --git a/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html b/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html
index 878ed5f..8a54657 100644
--- a/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html
+++ b/LayoutTests/fast/events/spatial-navigation/snav-iframe-with-offscreen-focusable-element.html
@@ -22,11 +22,19 @@
       ["Down", "1"],
       ["Down", "1"],
       ["Down", "1"],
+      ["Down", "1"],
+      ["Down", "1"],
+      ["Down", "1"],
+      ["Down", "1"],
       ["Down", "2"],
       ["Down", "end"],
       ["Up", "2"],
       ["Up", "2"],
       ["Up", "2"],
+      ["Up", "2"],
+      ["Up", "2"],
+      ["Up", "2"],
+      ["Up", "2"],
       ["Up", "1"],
       ["Up", "start"],
       ["DONE", "DONE"]
@@ -58,18 +66,19 @@
     <script src="../js/resources/js-test-post.js"></script>
   </head>
   <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-    <div><a id="start" href="a">a</a></div>
+    <div><a id="start" href="a"><img src="resources/green.png" width=30 height=30></a></div>
 
     <iframe width="80" height="80" scrolling="auto" src="data:text/html,
       <body>
-        <a id='1' href='a'>b</a>
-        <div style='margin-top:120px'>
-          <a id='2' href='a'>d</a>
+        <a id='1' href='a'><img src='no_image' width=30 height=30></a>
+        <div>
+          <img src='no_image' width=50 height=300>
+          <a id='2' href='a'><img src='no_image' width=30 height=30></a>
         </div>
       </body>
     "></iframe><br>
 
-    <div><a id="end" href="a">e</a></div>
+    <div><a id="end" href="a"><img src="resources/green.png" width=30 height=30></a></div>
     <div id="console"></div>
   </body>
 </html>
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index c3b57a2..7b8722d 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,59 @@
+2010-11-22  Yael Aharon  <yael.aharon at nokia.com>, Chang Shu  <chang.shu at nokia.com>
+
+        Reviewed by Antonio Gomes.
+
+        Spatial Navigation: issues with the node selection algorithm.
+        https://bugs.webkit.org/show_bug.cgi?id=49382
+
+        Modify the Spatial Navigation algorithm, to better handle initial focus and
+        navigation between frames.
+        The new algorithm takes the rect of the focused node as the startingRect,
+        instead of the node itself. That allows us to construct a virtual rect if
+        there is no focused node, or if it is off the screen.
+        The virtual rect is the edge of the container in the direction of the navigation.
+
+        With this patch, scrollable containers and frames will scroll regardless of weather
+        they have focusable content. Users will be able to use arrow keys to view all the
+        content of such a container. The only exception is if the container has style overflow:hidden. 
+        We will not scroll in that case.
+
+        With this patch, we handle z-index and positioning so that if there are 2 overlapping focusable nodes,
+        we do a hit test and only the node on top can get focus.
+
+        hasOffScreenRect() was modified so that it can check if a node will be off-screen even after we scrolled
+        its parent container. We do not add the scrolling conditions for containers that have overflow:hidden
+        and cannot scroll.
+
+        calculateScrollbarModesForLayout is used to decide if a frame can scroll or not. We cannot rely on
+        the exsistance of the scrollbar, because it could be removed via the API, while the frame is still
+        allowed to scroll.
+
+        * page/FocusController.cpp:
+        (WebCore::updateFocusCandidateIfNeeded):
+        (WebCore::FocusController::findFocusCandidateInContainer):
+        (WebCore::FocusController::advanceFocusDirectionallyInContainer):
+        (WebCore::FocusController::advanceFocusDirectionally):
+        * page/FocusController.h:
+        * page/FrameView.h:
+        * page/SpatialNavigation.cpp:
+        (WebCore::FocusCandidate::FocusCandidate):
+        (WebCore::distanceDataForNode):
+        (WebCore::alignmentForRects):
+        (WebCore::areRectsMoreThanFullScreenApart):
+        (WebCore::isRectInDirection):
+        (WebCore::hasOffscreenRect):
+        (WebCore::scrollInDirection):
+        (WebCore::isScrollableContainerNode):
+        (WebCore::scrollableEnclosingBoxOrParentFrameForNodeInDirection):
+        (WebCore::canScrollInDirection):
+        (WebCore::rectToAbsoluteCoordinates):
+        (WebCore::nodeRectInAbsoluteCoordinates):
+        (WebCore::frameRectInAbsoluteCoordinates):
+        (WebCore::entryAndExitPointsForDirection):
+        (WebCore::canBeScrolledIntoView):
+        (WebCore::virtualRectForDirection):
+        * page/SpatialNavigation.h:
+
 2010-11-22  Nikolas Zimmermann  <nzimmermann at rim.com>
 
         Reviewed by Dirk Schulze.
diff --git a/WebCore/page/FocusController.cpp b/WebCore/page/FocusController.cpp
index 2866019..dd999c6 100644
--- a/WebCore/page/FocusController.cpp
+++ b/WebCore/page/FocusController.cpp
@@ -40,11 +40,13 @@
 #include "Frame.h"
 #include "FrameTree.h"
 #include "FrameView.h"
+#include "HitTestResult.h"
 #include "HTMLFrameOwnerElement.h"
 #include "HTMLNames.h"
 #include "KeyboardEvent.h"
 #include "Page.h"
 #include "Range.h"
+#include "RenderLayer.h"
 #include "RenderObject.h"
 #include "RenderWidget.h"
 #include "SelectionController.h"
@@ -57,6 +59,7 @@ namespace WebCore {
 using namespace HTMLNames;
 using namespace std;
 
+static void updateFocusCandidateIfNeeded(FocusDirection direction, const IntRect& startingRect, FocusCandidate& candidate, FocusCandidate& closest);
 static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool focused)
 {
     // If we have a focused node we should dispatch blur on it before we blur the window.
@@ -289,64 +292,6 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb
     return true;
 }
 
-bool FocusController::advanceFocusDirectionally(FocusDirection direction, KeyboardEvent* event)
-{
-    Frame* frame = focusedOrMainFrame();
-    ASSERT(frame);
-    Document* focusedDocument = frame->document();
-    if (!focusedDocument)
-        return false;
-
-    Node* focusedNode = focusedDocument->focusedNode();
-    if (!focusedNode) {
-        // Just move to the first focusable node.
-        FocusDirection tabDirection = (direction == FocusDirectionUp || direction == FocusDirectionLeft) ?
-                                       FocusDirectionBackward : FocusDirectionForward;
-        // 'initialFocus' is set to true so the chrome is not focused.
-        return advanceFocusInDocumentOrder(tabDirection, event, true);
-    }
-
-    // Move up in the chain of nested frames.
-    frame = frame->tree()->top();
-
-    FocusCandidate focusCandidate;
-    findFocusableNodeInDirection(frame->document()->firstChild(), focusedNode, direction, event, focusCandidate);
-
-    Node* node = focusCandidate.node;
-    if (!node || !node->isElementNode()) {
-        // FIXME: May need a way to focus a document here.
-        Frame* frame = focusedOrMainFrame();
-        scrollInDirection(frame, direction);
-        return false;
-    }
-
-    // In order to avoid crazy jump between links that are either far away from each other,
-    // or just not currently visible, lets do a scroll in the given direction and bail out
-    // if |node| element is not in the viewport.
-    if (hasOffscreenRect(node)) {
-        Frame* frame = node->document()->view()->frame();
-        scrollInDirection(frame, direction, focusCandidate);
-        return true;
-    }
-
-    Document* newDocument = node->document();
-
-    if (newDocument != focusedDocument) {
-        // Focus is going away from the originally focused document, so clear the focused node.
-        focusedDocument->setFocusedNode(0);
-    }
-
-    if (newDocument)
-        setFocusedFrame(newDocument->frame());
-
-    Element* element = static_cast<Element*>(node);
-    ASSERT(element);
-
-    scrollIntoView(element);
-    element->focus(false);
-    return true;
-}
-
 static void updateFocusCandidateInSameContainer(const FocusCandidate& candidate, FocusCandidate& closest)
 {
     if (closest.isNull()) {
@@ -658,4 +603,180 @@ void FocusController::setActive(bool active)
         dispatchEventsOnWindowAndFocusedNode(m_focusedFrame->document(), active);
 }
 
+void updateFocusCandidateIfNeeded(FocusDirection direction, const IntRect& startingRect, FocusCandidate& candidate, FocusCandidate& closest)
+{
+    if (!candidate.node->isElementNode() || !candidate.node->renderer())
+        return;
+
+    // Ignore iframes that don't have a src attribute
+    if (candidate.node->isFrameOwnerElement() && !static_cast<HTMLFrameOwnerElement*>(candidate.node)->contentFrame())
+        return;
+
+    // Ignore off screen child nodes of containers that do not scroll (overflow:hidden)
+    if (hasOffscreenRect(candidate.node) && !canBeScrolledIntoView(direction, candidate))
+        return;
+
+    FocusCandidate current;
+    current.rect = startingRect;
+    distanceDataForNode(direction, current, candidate);
+    if (candidate.distance == maxDistance())
+        return;
+
+    if (hasOffscreenRect(candidate.node, direction) && candidate.alignment < Full)
+        return;
+
+    if (closest.isNull()) {
+        closest = candidate;
+        return;
+    }
+
+    IntRect intersectionRect = intersection(nodeRectInAbsoluteCoordinates(candidate.node, true), nodeRectInAbsoluteCoordinates(closest.node, true));
+    if (!intersectionRect.isEmpty()) {
+        // If 2 nodes are intersecting, do hit test to find which node in on top.
+        int x = intersectionRect.x() + intersectionRect.width() / 2;
+        int y = intersectionRect.y() + intersectionRect.height() / 2;
+        HitTestResult result = candidate.node->document()->page()->mainFrame()->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), false, true);
+        if (candidate.node->contains(result.innerNode())) {
+            closest = candidate;
+            return;
+        }
+        if (closest.node->contains(result.innerNode()))
+            return;
+    }
+
+    if (candidate.alignment == closest.alignment) {
+        if (candidate.distance < closest.distance)
+            closest = candidate;
+        return;
+    }
+
+    if (candidate.alignment > closest.alignment)
+        closest = candidate;
+}
+
+void FocusController::findFocusCandidateInContainer(Node* container, const IntRect& startingRect, FocusDirection direction, KeyboardEvent* event, FocusCandidate& closest)
+{
+    ASSERT(container);
+    Node* focusedNode = (focusedFrame() && focusedFrame()->document()) ? focusedFrame()->document()->focusedNode() : 0;
+
+    Node* node = container->firstChild();
+    for (; node; node = (node->isFrameOwnerElement() || canScrollInDirection(direction, node)) ? node->traverseNextSibling(container) : node->traverseNextNode(container)) {
+        if (node == focusedNode)
+            continue;
+
+        if (!node->renderer())
+            continue;
+
+        if (!node->isKeyboardFocusable(event) && !node->isFrameOwnerElement() && !canScrollInDirection(direction, node))
+            continue;
+
+        FocusCandidate candidate(node);
+        candidate.enclosingScrollableBox = container;
+        updateFocusCandidateIfNeeded(direction, startingRect, candidate, closest);
+    }
+}
+
+bool FocusController::advanceFocusDirectionallyInContainer(Node* container, const IntRect& startingRect, FocusDirection direction, KeyboardEvent* event)
+{
+    if (!container || !container->document())
+        return false;
+
+    IntRect newStartingRect = startingRect;
+
+    if (startingRect.isEmpty())
+        newStartingRect = virtualRectForDirection(direction, nodeRectInAbsoluteCoordinates(container));
+
+    // Find the closest node within current container in the direction of the navigation.
+    FocusCandidate focusCandidate;
+    findFocusCandidateInContainer(container, newStartingRect, direction, event, focusCandidate);
+
+    if (focusCandidate.isNull()) {
+        if (canScrollInDirection(direction, container)) {
+            // Nothing to focus, scroll if possible.
+            scrollInDirection(container, direction);
+            return true;
+        }
+        // Return false will cause a re-try, skipping this container.
+        return false;
+    }
+    if (focusCandidate.node->isFrameOwnerElement()) {
+        HTMLFrameOwnerElement* frameElement = static_cast<HTMLFrameOwnerElement*>(focusCandidate.node);
+        // If we have an iframe without the src attribute, it will not have a contentFrame().
+        // We ASSERT here to make sure that
+        // updateFocusCandidateIfNeeded() will never consider such an iframe as a candidate.
+        ASSERT(frameElement->contentFrame());
+
+        if (hasOffscreenRect(focusCandidate.node, direction)) {
+            scrollInDirection(focusCandidate.node->document(), direction);
+            return true;
+        }
+        // Navigate into a new frame.
+        IntRect rect;
+        Node* focusedNode = focusedOrMainFrame()->document()->focusedNode();
+        if (focusedNode && !hasOffscreenRect(focusedNode))
+            rect = nodeRectInAbsoluteCoordinates(focusedNode, true /* ignore border */);
+        frameElement->contentFrame()->document()->updateLayoutIgnorePendingStylesheets();
+        if (!advanceFocusDirectionallyInContainer(frameElement->contentFrame()->document(), rect, direction, event)) {
+            // The new frame had nothing interesting, need to find another candidate.
+            return advanceFocusDirectionallyInContainer(container, nodeRectInAbsoluteCoordinates(focusCandidate.node, true), direction, event);
+        }
+        return true;
+    }
+    if (canScrollInDirection(direction, focusCandidate.node)) {
+        if (hasOffscreenRect(focusCandidate.node, direction)) {
+            scrollInDirection(focusCandidate.node, direction);
+            return true;
+        }
+        // Navigate into a new scrollable container.
+        IntRect startingRect;
+        Node* focusedNode = focusedOrMainFrame()->document()->focusedNode();
+        if (focusedNode && !hasOffscreenRect(focusedNode))
+            startingRect = nodeRectInAbsoluteCoordinates(focusedNode, true);
+        return advanceFocusDirectionallyInContainer(focusCandidate.node, startingRect, direction, event);
+    }
+    if (hasOffscreenRect(focusCandidate.node, direction)) {
+        Node* container = focusCandidate.enclosingScrollableBox;
+        scrollInDirection(container, direction);
+        return true;
+    }
+
+    // We found a new focus node, navigate to it.
+    Element* element = toElement(focusCandidate.node);
+    ASSERT(element);
+
+    element->focus(false);
+    return true;
+}
+
+bool FocusController::advanceFocusDirectionally(FocusDirection direction, KeyboardEvent* event)
+{
+    Frame* curFrame = focusedOrMainFrame();
+    ASSERT(curFrame);
+
+    Document* focusedDocument = curFrame->document();
+    if (!focusedDocument)
+        return false;
+
+    Node* focusedNode = focusedDocument->focusedNode();
+    Node* container = focusedDocument;
+
+    // Figure out the starting rect.
+    IntRect startingRect;
+    if (focusedNode && !hasOffscreenRect(focusedNode)) {
+        container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, focusedNode);
+        startingRect = nodeRectInAbsoluteCoordinates(focusedNode, true /* ignore border */);
+    }
+
+    bool consumed = false;
+    do {
+        if (container->isDocumentNode())
+            static_cast<Document*>(container)->updateLayoutIgnorePendingStylesheets();
+        consumed = advanceFocusDirectionallyInContainer(container, startingRect, direction, event);
+        startingRect = nodeRectInAbsoluteCoordinates(container, true /* ignore border */);
+        container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, container);
+    } while (!consumed && container);
+
+    return consumed;
+}
+
 } // namespace WebCore
diff --git a/WebCore/page/FocusController.h b/WebCore/page/FocusController.h
index 050a08f..883c993 100644
--- a/WebCore/page/FocusController.h
+++ b/WebCore/page/FocusController.h
@@ -68,6 +68,9 @@ private:
                                       const FocusCandidate& parentCandidate = FocusCandidate());
     void deepFindFocusableNodeInDirection(Node* container, Node* focused, FocusDirection, KeyboardEvent*, FocusCandidate&);
 
+    bool advanceFocusDirectionallyInContainer(Node* container, const IntRect& startingRect, FocusDirection, KeyboardEvent*);
+    void findFocusCandidateInContainer(Node* container, const IntRect& startingRect, FocusDirection, KeyboardEvent*, FocusCandidate& closest);
+
     Page* m_page;
     RefPtr<Frame> m_focusedFrame;
     bool m_isActive;
diff --git a/WebCore/page/FrameView.h b/WebCore/page/FrameView.h
index 17b147e..bac24de 100644
--- a/WebCore/page/FrameView.h
+++ b/WebCore/page/FrameView.h
@@ -236,6 +236,8 @@ public:
     bool isFrameViewScrollCorner(RenderScrollbarPart* scrollCorner) const { return m_scrollCorner == scrollCorner; }
     void invalidateScrollCorner();
 
+    void calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode);
+
     // Normal delay
     static void setRepaintThrottlingDeferredRepaintDelay(double p);
     // Negative value would mean that first few repaints happen without a delay
@@ -265,7 +267,6 @@ private:
     bool hasFixedObjects() const { return m_fixedObjectCount > 0; }
 
     void applyOverflowToViewport(RenderObject*, ScrollbarMode& hMode, ScrollbarMode& vMode);
-    void calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode);
 
     void updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow);
 
diff --git a/WebCore/page/SpatialNavigation.cpp b/WebCore/page/SpatialNavigation.cpp
index a42435f..218fca3 100644
--- a/WebCore/page/SpatialNavigation.cpp
+++ b/WebCore/page/SpatialNavigation.cpp
@@ -43,12 +43,27 @@ namespace WebCore {
 
 static long long spatialDistance(FocusDirection, const IntRect&, const IntRect&);
 static IntRect renderRectRelativeToRootDocument(RenderObject*);
-static RectsAlignment alignmentForRects(FocusDirection, const IntRect&, const IntRect&);
+static RectsAlignment alignmentForRects(FocusDirection, const IntRect&, const IntRect&, const IntSize& viewSize);
 static bool areRectsFullyAligned(FocusDirection, const IntRect&, const IntRect&);
 static bool areRectsPartiallyAligned(FocusDirection, const IntRect&, const IntRect&);
+static bool areRectsMoreThanFullScreenApart(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect, const IntSize& viewSize);
 static bool isRectInDirection(FocusDirection, const IntRect&, const IntRect&);
 static void deflateIfOverlapped(IntRect&, IntRect&);
 static bool checkNegativeCoordsForNode(Node*, const IntRect&);
+static IntRect rectToAbsoluteCoordinates(Frame* initialFrame, const IntRect& rect);
+static void entryAndExitPointsForDirection(FocusDirection direction, const IntRect& startingRect, const IntRect& potentialRect, IntPoint& exitPoint, IntPoint& entryPoint);
+
+
+FocusCandidate::FocusCandidate(Node* n)
+    : node(n)
+    , enclosingScrollableBox(0)
+    , distance(maxDistance())
+    , parentDistance(maxDistance())
+    , alignment(None)
+    , parentAlignment(None)
+    , rect(nodeRectInAbsoluteCoordinates(n, true /* ignore border */))
+{
+}
 
 bool isSpatialNavigationEnabled(const Frame* frame)
 {
@@ -102,7 +117,8 @@ void distanceDataForNode(FocusDirection direction, Node* start, FocusCandidate&
     // The distance between two nodes is not to be considered alone when evaluating/looking
     // for the best focus candidate node. Alignment of rects can be also a good point to be
     // considered in order to make the algorithm to behavior in a more intuitive way.
-    candidate.alignment = alignmentForRects(direction, curRect, targetRect);
+    IntSize viewSize = candidate.node->document()->page()->mainFrame()->view()->visibleContentRect().size();
+    candidate.alignment = alignmentForRects(direction, curRect, targetRect, viewSize);
     candidate.distance = spatialDistance(direction, curRect, targetRect);
 }
 
@@ -136,8 +152,12 @@ static IntRect renderRectRelativeToRootDocument(RenderObject* render)
     return rect;
 }
 
-static RectsAlignment alignmentForRects(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect)
+static RectsAlignment alignmentForRects(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect, const IntSize& viewSize)
 {
+    // If we found a node in full alignment, but it is too far away, ignore it.
+    if (areRectsMoreThanFullScreenApart(direction, curRect, targetRect, viewSize))
+        return None;
+
     if (areRectsFullyAligned(direction, curRect, targetRect))
         return Full;
 
@@ -277,6 +297,25 @@ static bool areRectsPartiallyAligned(FocusDirection direction, const IntRect& a,
             || (bEnd >= aStart && bEnd <= aEnd));
 }
 
+static bool areRectsMoreThanFullScreenApart(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect, const IntSize& viewSize)
+{
+    ASSERT(isRectInDirection(direction, curRect, targetRect));
+
+    switch (direction) {
+    case FocusDirectionLeft:
+        return curRect.x() - targetRect.right() > viewSize.width();
+    case FocusDirectionRight:
+        return targetRect.x() - curRect.right() > viewSize.width();
+    case FocusDirectionUp:
+        return curRect.y() - targetRect.bottom() > viewSize.height();
+    case FocusDirectionDown:
+        return targetRect.y() - curRect.bottom() > viewSize.height();
+    default:
+        ASSERT_NOT_REACHED();
+        return true;
+    }
+}
+
 // Return true if rect |a| is below |b|. False otherwise.
 static inline bool below(const IntRect& a, const IntRect& b)
 {
@@ -403,29 +442,25 @@ static long long spatialDistance(FocusDirection direction, const IntRect& a, con
 
 static bool isRectInDirection(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect)
 {
-    IntPoint center(targetRect.center());
-    int targetMiddle = isHorizontalMove(direction) ? center.x() : center.y();
-
     switch (direction) {
     case FocusDirectionLeft:
-        return targetMiddle < curRect.x();
+        return targetRect.right() <= curRect.x();
     case FocusDirectionRight:
-        return targetMiddle > curRect.right();
+        return targetRect.x() >= curRect.right();
     case FocusDirectionUp:
-        return targetMiddle < curRect.y();
+        return targetRect.bottom() <= curRect.y();
     case FocusDirectionDown:
-        return targetMiddle > curRect.bottom();
+        return targetRect.y() >= curRect.bottom();
     default:
         ASSERT_NOT_REACHED();
+        return false;
     }
-
-    return false;
 }
 
 // Checks if |node| is offscreen the visible area (viewport) of its container
 // document. In case it is, one can scroll in direction or take any different
 // desired action later on.
-bool hasOffscreenRect(Node* node)
+bool hasOffscreenRect(Node* node, FocusDirection direction)
 {
     // Get the FrameView in which |node| is (which means the current viewport if |node|
     // is not in an inner document), so we can check if its content rect is visible
@@ -435,6 +470,28 @@ bool hasOffscreenRect(Node* node)
         return true;
 
     IntRect containerViewportRect = frameView->visibleContentRect();
+    // We want to select a node if it is currently off screen, but will be
+    // exposed after we scroll. Adjust the viewport to post-scrolling position.
+    // If the container has overflow:hidden, we cannot scroll, so we do not pass direction
+    // and we do not adjust for scrolling.
+    switch (direction) {
+    case FocusDirectionLeft:
+        containerViewportRect.setX(containerViewportRect.x() - Scrollbar::pixelsPerLineStep());
+        containerViewportRect.setWidth(containerViewportRect.width() + Scrollbar::pixelsPerLineStep());
+        break;
+    case FocusDirectionRight:
+        containerViewportRect.setWidth(containerViewportRect.width() + Scrollbar::pixelsPerLineStep());
+        break;
+    case FocusDirectionUp:
+        containerViewportRect.setY(containerViewportRect.y() - Scrollbar::pixelsPerLineStep());
+        containerViewportRect.setHeight(containerViewportRect.height() + Scrollbar::pixelsPerLineStep());
+        break;
+    case FocusDirectionDown:
+        containerViewportRect.setHeight(containerViewportRect.height() + Scrollbar::pixelsPerLineStep());
+        break;
+    default:
+        break;
+    }
 
     RenderObject* render = node->renderer();
     if (!render)
@@ -447,36 +504,72 @@ bool hasOffscreenRect(Node* node)
     return !containerViewportRect.intersects(rect);
 }
 
-// In a bottom-up way, this method tries to scroll |frame| in a given direction
-// |direction|, going up in the frame tree hierarchy in case it does not succeed.
-bool scrollInDirection(Frame* frame, FocusDirection direction, const FocusCandidate& candidate)
+bool scrollInDirection(Frame* frame, FocusDirection direction)
 {
-    if (!frame)
-        return false;
+    ASSERT(frame && canScrollInDirection(direction, frame->document()));
+
+    if (frame && canScrollInDirection(direction, frame->document())) {
+        int dx = 0;
+        int dy = 0;
+        switch (direction) {
+        case FocusDirectionLeft:
+            dx = - Scrollbar::pixelsPerLineStep();
+            break;
+        case FocusDirectionRight:
+            dx = Scrollbar::pixelsPerLineStep();
+            break;
+        case FocusDirectionUp:
+            dy = - Scrollbar::pixelsPerLineStep();
+            break;
+        case FocusDirectionDown:
+            dy = Scrollbar::pixelsPerLineStep();
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            return false;
+        }
 
-    ScrollDirection scrollDirection;
+        frame->view()->scrollBy(IntSize(dx, dy));
+        return true;
+    }
+    return false;
+}
 
-    switch (direction) {
-    case FocusDirectionLeft:
-        scrollDirection = ScrollLeft;
-        break;
-    case FocusDirectionRight:
-        scrollDirection = ScrollRight;
-        break;
-    case FocusDirectionUp:
-        scrollDirection = ScrollUp;
-        break;
-    case FocusDirectionDown:
-        scrollDirection = ScrollDown;
-        break;
-    default:
+bool scrollInDirection(Node* container, FocusDirection direction)
+{
+    if (container->isDocumentNode())
+        return scrollInDirection(static_cast<Document*>(container)->frame(), direction);
+
+    if (!container->renderBox())
         return false;
-    }
 
-    if (!candidate.isNull() && isScrollableContainerNode(candidate.enclosingScrollableBox))
-        return frame->eventHandler()->scrollRecursively(scrollDirection, ScrollByLine, candidate.enclosingScrollableBox);
+    if (container && canScrollInDirection(direction, container)) {
+        int dx = 0;
+        int dy = 0;
+        switch (direction) {
+        case FocusDirectionLeft:
+            dx = - min(Scrollbar::pixelsPerLineStep(), container->renderBox()->scrollLeft());
+            break;
+        case FocusDirectionRight:
+            ASSERT(container->renderBox()->scrollWidth() > (container->renderBox()->scrollLeft() + container->renderBox()->clientWidth()));
+            dx = min(Scrollbar::pixelsPerLineStep(), container->renderBox()->scrollWidth() - (container->renderBox()->scrollLeft() + container->renderBox()->clientWidth()));
+            break;
+        case FocusDirectionUp:
+            dy = - min(Scrollbar::pixelsPerLineStep(), container->renderBox()->scrollTop());
+            break;
+        case FocusDirectionDown:
+            ASSERT(container->renderBox()->scrollHeight() - (container->renderBox()->scrollTop() + container->renderBox()->clientHeight()));
+            dy = min(Scrollbar::pixelsPerLineStep(), container->renderBox()->scrollHeight() - (container->renderBox()->scrollTop() + container->renderBox()->clientHeight()));
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            return false;
+        }
 
-    return frame->eventHandler()->scrollRecursively(scrollDirection, ScrollByLine);
+        container->renderBox()->enclosingLayer()->scrollByRecursively(dx, dy);
+        return true;
+    }
+    return false;
 }
 
 void scrollIntoView(Element* element)
@@ -534,7 +627,7 @@ static bool checkNegativeCoordsForNode(Node* node, const IntRect& curRect)
     return canBeScrolled;
 }
 
-bool isScrollableContainerNode(Node* node)
+bool isScrollableContainerNode(const Node* node)
 {
     if (!node)
         return false;
@@ -567,4 +660,268 @@ bool isNodeDeepDescendantOfDocument(Node* node, Document* baseDocument)
     return descendant;
 }
 
+Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusDirection direction, Node* node)
+{
+    ASSERT(node);
+    Node* parent = node;
+    do {
+        if (parent->isDocumentNode())
+            parent = static_cast<Document*>(parent)->document()->frame()->ownerElement();
+        else
+            parent = parent->parentNode();
+    } while (parent && !canScrollInDirection(direction, parent) && !parent->isDocumentNode());
+
+    return parent;
+}
+
+bool canScrollInDirection(FocusDirection direction, const Node* container)
+{
+    ASSERT(container);
+    if (container->isDocumentNode())
+        return canScrollInDirection(direction, static_cast<const Document*>(container)->frame());
+
+    if (!isScrollableContainerNode(container))
+        return false;
+
+    switch (direction) {
+    case FocusDirectionLeft:
+        return (container->renderer()->style()->overflowX() != OHIDDEN && container->renderBox()->scrollLeft() > 0);
+    case FocusDirectionUp:
+        return (container->renderer()->style()->overflowY() != OHIDDEN && container->renderBox()->scrollTop() > 0);
+    case FocusDirectionRight:
+        return (container->renderer()->style()->overflowX() != OHIDDEN && container->renderBox()->scrollLeft() + container->renderBox()->clientWidth() < container->renderBox()->scrollWidth());
+    case FocusDirectionDown:
+        return (container->renderer()->style()->overflowY() != OHIDDEN && container->renderBox()->scrollTop() + container->renderBox()->clientHeight() < container->renderBox()->scrollHeight());
+    default:
+        ASSERT_NOT_REACHED();
+        return false;
+    }
+}
+
+bool canScrollInDirection(FocusDirection direction, const Frame* frame)
+{
+    if (!frame->view())
+        return false;
+    ScrollbarMode verticalMode;
+    ScrollbarMode horizontalMode;
+    frame->view()->calculateScrollbarModesForLayout(horizontalMode, verticalMode);
+    if ((direction == FocusDirectionLeft || direction == FocusDirectionRight) && ScrollbarAlwaysOff == horizontalMode)
+        return false;
+    if ((direction == FocusDirectionUp || direction == FocusDirectionDown) &&  ScrollbarAlwaysOff == verticalMode)
+        return false;
+    IntSize size = frame->view()->contentsSize();
+    IntSize offset = frame->view()->scrollOffset();
+    IntRect rect = frame->view()->visibleContentRect(true);
+
+    switch (direction) {
+    case FocusDirectionLeft:
+        return offset.width() > 0;
+    case FocusDirectionUp:
+        return offset.height() > 0;
+    case FocusDirectionRight:
+        return rect.width() + offset.width() < size.width();
+    case FocusDirectionDown:
+        return rect.height() + offset.height() < size.height();
+    default:
+        ASSERT_NOT_REACHED();
+        return false;
+    }
+}
+
+static IntRect rectToAbsoluteCoordinates(Frame* initialFrame, const IntRect& initialRect)
+{
+    IntRect rect = initialRect;
+    for (Frame* frame = initialFrame; frame; frame = frame->tree()->parent()) {
+        if (Element* element = static_cast<Element*>(frame->ownerElement())) {
+            do {
+                rect.move(element->offsetLeft(), element->offsetTop());
+            } while ((element = element->offsetParent()));
+            rect.move((-frame->view()->scrollOffset()));
+        }
+    }
+    return rect;
+}
+
+IntRect nodeRectInAbsoluteCoordinates(Node* node, bool ignoreBorder)
+{
+    ASSERT(node && node->renderer());
+
+    if (node->isDocumentNode())
+        return frameRectInAbsoluteCoordinates(static_cast<Document*>(node)->frame());
+    IntRect rect = rectToAbsoluteCoordinates(node->document()->frame(), node->getRect());
+
+    // For authors that use border instead of outline in their CSS, we compensate by ignoring the border when calculating
+    // the rect of the focused element.
+    if (ignoreBorder) {
+        rect.move(node->renderer()->style()->borderLeftWidth(), node->renderer()->style()->borderTopWidth());
+        rect.setWidth(rect.width() - node->renderer()->style()->borderLeftWidth() - node->renderer()->style()->borderRightWidth());
+        rect.setHeight(rect.height() - node->renderer()->style()->borderTopWidth() - node->renderer()->style()->borderBottomWidth());
+    }
+    return rect;
+}
+
+IntRect frameRectInAbsoluteCoordinates(Frame* frame)
+{
+    return rectToAbsoluteCoordinates(frame, frame->view()->visibleContentRect());
+}
+
+// This method calculates the exitPoint from the startingRect and the entryPoint into the candidate rect.
+// The line between those 2 points is the closest distance between the 2 rects.
+void entryAndExitPointsForDirection(FocusDirection direction, const IntRect& startingRect, const IntRect& potentialRect, IntPoint& exitPoint, IntPoint& entryPoint)
+{
+    switch (direction) {
+    case FocusDirectionLeft:
+        exitPoint.setX(startingRect.x());
+        entryPoint.setX(potentialRect.right());
+        break;
+    case FocusDirectionUp:
+        exitPoint.setY(startingRect.y());
+        entryPoint.setY(potentialRect.bottom());
+        break;
+    case FocusDirectionRight:
+        exitPoint.setX(startingRect.right());
+        entryPoint.setX(potentialRect.x());
+        break;
+    case FocusDirectionDown:
+        exitPoint.setY(startingRect.bottom());
+        entryPoint.setY(potentialRect.y());
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+
+    switch (direction) {
+    case FocusDirectionLeft:
+    case FocusDirectionRight:
+        if (below(startingRect, potentialRect)) {
+            exitPoint.setY(startingRect.y());
+            entryPoint.setY(potentialRect.bottom());
+        } else if (below(potentialRect, startingRect)) {
+            exitPoint.setY(startingRect.bottom());
+            entryPoint.setY(potentialRect.y());
+        } else {
+            exitPoint.setY(max(startingRect.y(), potentialRect.y()));
+            entryPoint.setY(exitPoint.y());
+        }
+        break;
+    case FocusDirectionUp:
+    case FocusDirectionDown:
+        if (rightOf(startingRect, potentialRect)) {
+            exitPoint.setX(startingRect.x());
+            entryPoint.setX(potentialRect.right());
+        } else if (rightOf(potentialRect, startingRect)) {
+            exitPoint.setX(startingRect.right());
+            entryPoint.setX(potentialRect.x());
+        } else {
+            exitPoint.setX(max(startingRect.x(), potentialRect.x()));
+            entryPoint.setX(exitPoint.x());
+        }
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+}
+
+void distanceDataForNode(FocusDirection direction, FocusCandidate& current, FocusCandidate& candidate)
+{
+    if (candidate.isNull())
+        return;
+    if (!candidate.node->renderer())
+        return;
+    IntRect nodeRect = candidate.rect;
+    IntRect currentRect = current.rect;
+    deflateIfOverlapped(currentRect, nodeRect);
+
+    if (!isRectInDirection(direction, currentRect, nodeRect))
+        return;
+
+    IntPoint exitPoint;
+    IntPoint entryPoint;
+    int sameAxisDistance = 0;
+    int otherAxisDistance = 0;
+    entryAndExitPointsForDirection(direction, currentRect, nodeRect, exitPoint, entryPoint);
+
+    switch (direction) {
+    case FocusDirectionLeft:
+        sameAxisDistance = exitPoint.x() - entryPoint.x();
+        otherAxisDistance = abs(exitPoint.y() - entryPoint.y());
+        break;
+    case FocusDirectionUp:
+        sameAxisDistance = exitPoint.y() - entryPoint.y();
+        otherAxisDistance = abs(exitPoint.x() - entryPoint.x());
+        break;
+    case FocusDirectionRight:
+        sameAxisDistance = entryPoint.x() - exitPoint.x();
+        otherAxisDistance = abs(entryPoint.y() - exitPoint.y());
+        break;
+    case FocusDirectionDown:
+        sameAxisDistance = entryPoint.y() - exitPoint.y();
+        otherAxisDistance = abs(entryPoint.x() - exitPoint.x());
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+        return;
+    }
+
+    int x = (entryPoint.x() - exitPoint.x()) * (entryPoint.x() - exitPoint.x());
+    int y = (entryPoint.y() - exitPoint.y()) * (entryPoint.y() - exitPoint.y());
+
+    float euclidianDistance = sqrt((x + y) * 1.0f);
+
+    // Loosely based on http://www.w3.org/TR/WICD/#focus-handling
+    // df = dotDist + dx + dy + 2 * (xdisplacement + ydisplacement) - sqrt(Overlap)
+
+    float distance = euclidianDistance + sameAxisDistance + 2 * otherAxisDistance;
+    candidate.distance = roundf(distance);
+    IntSize viewSize = candidate.node->document()->page()->mainFrame()->view()->visibleContentRect().size();
+    candidate.alignment = alignmentForRects(direction, currentRect, nodeRect, viewSize);
+}
+
+bool canBeScrolledIntoView(FocusDirection direction, FocusCandidate& candidate)
+{
+    ASSERT(candidate.node && hasOffscreenRect(candidate.node));
+    IntRect candidateRect = candidate.rect;
+    for (Node* parentNode = candidate.node->parent(); parentNode; parentNode = parentNode->parent()) {
+        IntRect parentRect = nodeRectInAbsoluteCoordinates(parentNode);
+        if (!candidateRect.intersects(parentRect)) {
+            if (((direction == FocusDirectionLeft || direction == FocusDirectionRight) && parentNode->renderer()->style()->overflowX() == OHIDDEN)
+                || ((direction == FocusDirectionUp || direction == FocusDirectionDown) && parentNode->renderer()->style()->overflowY() == OHIDDEN))
+                return false;
+        }
+        if (parentNode == candidate.enclosingScrollableBox)
+            return canScrollInDirection(direction, parentNode);
+    }
+    return true;
+}
+
+// The starting rect is the rect of the focused node, in document coordinates.
+// Compose a virtual starting rect if there is no focused node or if it is off screen.
+// The virtual rect is the edge of the container or frame. We select which
+// edge depending on the direction of the navigation.
+IntRect virtualRectForDirection(FocusDirection direction, const IntRect& startingRect)
+{
+    IntRect virtualStartingRect = startingRect;
+    switch (direction) {
+    case FocusDirectionLeft:
+        virtualStartingRect.setX(virtualStartingRect.right());
+        virtualStartingRect.setWidth(0);
+        break;
+    case FocusDirectionUp:
+        virtualStartingRect.setY(virtualStartingRect.bottom());
+        virtualStartingRect.setHeight(0);
+        break;
+    case FocusDirectionRight:
+        virtualStartingRect.setWidth(0);
+        break;
+    case FocusDirectionDown:
+        virtualStartingRect.setHeight(0);
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+
+    return virtualStartingRect;
+}
+
+
 } // namespace WebCore
diff --git a/WebCore/page/SpatialNavigation.h b/WebCore/page/SpatialNavigation.h
index 45b0155..2aa03b6 100644
--- a/WebCore/page/SpatialNavigation.h
+++ b/WebCore/page/SpatialNavigation.h
@@ -22,6 +22,7 @@
 #define SpatialNavigation_h
 
 #include "FocusDirection.h"
+#include "IntRect.h"
 #include "Node.h"
 
 #include <limits>
@@ -107,16 +108,7 @@ struct FocusCandidate {
     {
     }
 
-    FocusCandidate(Node* n)
-        : node(n)
-        , enclosingScrollableBox(0)
-        , distance(maxDistance())
-        , parentDistance(maxDistance())
-        , alignment(None)
-        , parentAlignment(None)
-    {
-    }
-
+    FocusCandidate(Node* n);
     bool isNull() const { return !node; }
     bool inScrollableContainer() const { return node && enclosingScrollableBox; }
     Document* document() const { return node ? node->document() : 0; }
@@ -127,16 +119,25 @@ struct FocusCandidate {
     long long parentDistance;
     RectsAlignment alignment;
     RectsAlignment parentAlignment;
+    IntRect rect;
 };
 
 void distanceDataForNode(FocusDirection direction, Node* start, FocusCandidate& candidate);
-bool scrollInDirection(Frame*, FocusDirection, const FocusCandidate& candidate = FocusCandidate());
+bool scrollInDirection(Frame*, FocusDirection);
+bool scrollInDirection(Node* container, FocusDirection);
 void scrollIntoView(Element*);
-bool hasOffscreenRect(Node*);
+bool hasOffscreenRect(Node*, FocusDirection direction = FocusDirectionNone);
 bool isInRootDocument(Node*);
-bool isScrollableContainerNode(Node*);
+bool isScrollableContainerNode(const Node*);
 bool isNodeDeepDescendantOfDocument(Node*, Document*);
-
+Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusDirection, Node* node);
+bool canScrollInDirection(FocusDirection, const Node* container);
+bool canScrollInDirection(FocusDirection, const Frame*);
+IntRect nodeRectInAbsoluteCoordinates(Node*, bool ignoreBorder = false);
+IntRect frameRectInAbsoluteCoordinates(Frame*);
+void distanceDataForNode(FocusDirection, FocusCandidate& current, FocusCandidate& candidate);
+bool canBeScrolledIntoView(FocusDirection, FocusCandidate&);
+IntRect virtualRectForDirection(FocusDirection, const IntRect& startingRect);
 } // namspace WebCore
 
 #endif // SpatialNavigation_h

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list