| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) | 2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) |
| 3 * Copyright (C) 2009 Antonio Gomes <tonikitoo@webkit.org> | 3 * Copyright (C) 2009 Antonio Gomes <tonikitoo@webkit.org> |
| 4 * | 4 * |
| 5 * All rights reserved. | 5 * All rights reserved. |
| 6 * | 6 * |
| 7 * Redistribution and use in source and binary forms, with or without | 7 * Redistribution and use in source and binary forms, with or without |
| 8 * modification, are permitted provided that the following conditions | 8 * modification, are permitted provided that the following conditions |
| 9 * are met: | 9 * are met: |
| 10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 #include "core/html/HTMLImageElement.h" | 38 #include "core/html/HTMLImageElement.h" |
| 39 #include "core/page/FrameTree.h" | 39 #include "core/page/FrameTree.h" |
| 40 #include "core/page/Page.h" | 40 #include "core/page/Page.h" |
| 41 #include "core/rendering/RenderLayer.h" | 41 #include "core/rendering/RenderLayer.h" |
| 42 #include "platform/geometry/IntRect.h" | 42 #include "platform/geometry/IntRect.h" |
| 43 | 43 |
| 44 namespace blink { | 44 namespace blink { |
| 45 | 45 |
| 46 using namespace HTMLNames; | 46 using namespace HTMLNames; |
| 47 | 47 |
| 48 static RectsAlignment alignmentForRects(WebFocusType, const LayoutRect&, const L
ayoutRect&, const LayoutSize& viewSize); | |
| 49 static bool areRectsFullyAligned(WebFocusType, const LayoutRect&, const LayoutRe
ct&); | |
| 50 static bool areRectsPartiallyAligned(WebFocusType, const LayoutRect&, const Layo
utRect&); | |
| 51 static bool areRectsMoreThanFullScreenApart(WebFocusType, const LayoutRect& curR
ect, const LayoutRect& targetRect, const LayoutSize& viewSize); | |
| 52 static bool isRectInDirection(WebFocusType, const LayoutRect&, const LayoutRect&
); | |
| 53 static void deflateIfOverlapped(LayoutRect&, LayoutRect&); | 48 static void deflateIfOverlapped(LayoutRect&, LayoutRect&); |
| 54 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo
utRect&); | 49 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo
utRect&); |
| 55 static bool isScrollableNode(const Node*); | 50 static bool isScrollableNode(const Node*); |
| 56 | 51 |
| 57 FocusCandidate::FocusCandidate(Node* node, WebFocusType type) | 52 FocusCandidate::FocusCandidate(Node* node, WebFocusType type) |
| 58 : visibleNode(nullptr) | 53 : visibleNode(nullptr) |
| 59 , focusableNode(nullptr) | 54 , focusableNode(nullptr) |
| 60 , enclosingScrollableBox(nullptr) | 55 , enclosingScrollableBox(nullptr) |
| 61 , distance(maxDistance()) | 56 , distance(maxDistance()) |
| 62 , alignment(None) | |
| 63 , isOffscreen(true) | 57 , isOffscreen(true) |
| 64 , isOffscreenAfterScrolling(true) | 58 , isOffscreenAfterScrolling(true) |
| 65 { | 59 { |
| 66 ASSERT(node); | 60 ASSERT(node); |
| 67 ASSERT(node->isElementNode()); | 61 ASSERT(node->isElementNode()); |
| 68 | 62 |
| 69 if (isHTMLAreaElement(*node)) { | 63 if (isHTMLAreaElement(*node)) { |
| 70 HTMLAreaElement& area = toHTMLAreaElement(*node); | 64 HTMLAreaElement& area = toHTMLAreaElement(*node); |
| 71 HTMLImageElement* image = area.imageElement(); | 65 HTMLImageElement* image = area.imageElement(); |
| 72 if (!image || !image->renderer()) | 66 if (!image || !image->renderer()) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 85 focusableNode = node; | 79 focusableNode = node; |
| 86 isOffscreen = hasOffscreenRect(visibleNode); | 80 isOffscreen = hasOffscreenRect(visibleNode); |
| 87 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type); | 81 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type); |
| 88 } | 82 } |
| 89 | 83 |
| 90 bool isSpatialNavigationEnabled(const LocalFrame* frame) | 84 bool isSpatialNavigationEnabled(const LocalFrame* frame) |
| 91 { | 85 { |
| 92 return (frame && frame->settings() && frame->settings()->spatialNavigationEn
abled()); | 86 return (frame && frame->settings() && frame->settings()->spatialNavigationEn
abled()); |
| 93 } | 87 } |
| 94 | 88 |
| 95 static RectsAlignment alignmentForRects(WebFocusType type, const LayoutRect& cur
Rect, const LayoutRect& targetRect, const LayoutSize& viewSize) | 89 static bool rectsIntersectOnOrthogonalAxis(WebFocusType type, const LayoutRect&
a, const LayoutRect& b) |
| 96 { | 90 { |
| 97 // If we found a node in full alignment, but it is too far away, ignore it. | |
| 98 if (areRectsMoreThanFullScreenApart(type, curRect, targetRect, viewSize)) | |
| 99 return None; | |
| 100 | |
| 101 if (areRectsFullyAligned(type, curRect, targetRect)) | |
| 102 return Full; | |
| 103 | |
| 104 if (areRectsPartiallyAligned(type, curRect, targetRect)) | |
| 105 return Partial; | |
| 106 | |
| 107 return None; | |
| 108 } | |
| 109 | |
| 110 static inline bool isHorizontalMove(WebFocusType type) | |
| 111 { | |
| 112 return type == WebFocusTypeLeft || type == WebFocusTypeRight; | |
| 113 } | |
| 114 | |
| 115 static inline LayoutUnit start(WebFocusType type, const LayoutRect& rect) | |
| 116 { | |
| 117 return isHorizontalMove(type) ? rect.y() : rect.x(); | |
| 118 } | |
| 119 | |
| 120 static inline LayoutUnit middle(WebFocusType type, const LayoutRect& rect) | |
| 121 { | |
| 122 LayoutPoint center(rect.center()); | |
| 123 return isHorizontalMove(type) ? center.y(): center.x(); | |
| 124 } | |
| 125 | |
| 126 static inline LayoutUnit end(WebFocusType type, const LayoutRect& rect) | |
| 127 { | |
| 128 return isHorizontalMove(type) ? rect.maxY() : rect.maxX(); | |
| 129 } | |
| 130 | |
| 131 // This method checks if rects |a| and |b| are fully aligned either vertically o
r | |
| 132 // horizontally. In general, rects whose central point falls between the top or | |
| 133 // bottom of each other are considered fully aligned. | |
| 134 // Rects that match this criteria are preferable target nodes in move focus chan
ging | |
| 135 // operations. | |
| 136 // * a = Current focused node's rect. | |
| 137 // * b = Focus candidate node's rect. | |
| 138 static bool areRectsFullyAligned(WebFocusType type, const LayoutRect& a, const L
ayoutRect& b) | |
| 139 { | |
| 140 LayoutUnit aStart, bStart, aEnd, bEnd; | |
| 141 | |
| 142 switch (type) { | 91 switch (type) { |
| 143 case WebFocusTypeLeft: | 92 case WebFocusTypeLeft: |
| 144 aStart = a.x(); | |
| 145 bEnd = b.x(); | |
| 146 break; | |
| 147 case WebFocusTypeRight: | 93 case WebFocusTypeRight: |
| 148 aStart = b.x(); | 94 return a.maxY() > b.y() && a.y() < b.maxY(); |
| 149 bEnd = a.x(); | |
| 150 break; | |
| 151 case WebFocusTypeUp: | 95 case WebFocusTypeUp: |
| 152 aStart = a.y(); | |
| 153 bEnd = b.y(); | |
| 154 break; | |
| 155 case WebFocusTypeDown: | 96 case WebFocusTypeDown: |
| 156 aStart = b.y(); | 97 return a.maxX() > b.x() && a.x() < b.maxX(); |
| 157 bEnd = a.y(); | |
| 158 break; | |
| 159 default: | 98 default: |
| 160 ASSERT_NOT_REACHED(); | 99 ASSERT_NOT_REACHED(); |
| 161 return false; | 100 return false; |
| 162 } | 101 } |
| 163 | |
| 164 if (aStart < bEnd) | |
| 165 return false; | |
| 166 | |
| 167 aStart = start(type, a); | |
| 168 bStart = start(type, b); | |
| 169 | |
| 170 LayoutUnit aMiddle = middle(type, a); | |
| 171 LayoutUnit bMiddle = middle(type, b); | |
| 172 | |
| 173 aEnd = end(type, a); | |
| 174 bEnd = end(type, b); | |
| 175 | |
| 176 // Picture of the totally aligned logic: | |
| 177 // | |
| 178 // Horizontal Vertical Horizontal Vertical | |
| 179 // **************************** ***************************** | |
| 180 // * _ * _ _ _ _ * * _ * _ _ * | |
| 181 // * |_| _ * |_|_|_|_| * * _ |_| * |_|_| * | |
| 182 // * |_|....|_| * . * * |_|....|_| * . * | |
| 183 // * |_| |_| (1) . * * |_| |_| (2) . * | |
| 184 // * |_| * _._ * * |_| * _ _._ _ * | |
| 185 // * * |_|_| * * * |_|_|_|_| * | |
| 186 // * * * * * * | |
| 187 // **************************** ***************************** | |
| 188 | |
| 189 return (bMiddle >= aStart && bMiddle <= aEnd) // (1) | |
| 190 || (aMiddle >= bStart && aMiddle <= bEnd); // (2) | |
| 191 } | |
| 192 | |
| 193 // This method checks if rects |a| and |b| are partially aligned either vertical
ly or | |
| 194 // horizontally. In general, rects whose either of edges falls between the top o
r | |
| 195 // bottom of each other are considered partially-aligned. | |
| 196 // This is a separate set of conditions from "fully-aligned" and do not include
cases | |
| 197 // that satisfy the former. | |
| 198 // * a = Current focused node's rect. | |
| 199 // * b = Focus candidate node's rect. | |
| 200 static bool areRectsPartiallyAligned(WebFocusType type, const LayoutRect& a, con
st LayoutRect& b) | |
| 201 { | |
| 202 LayoutUnit aStart = start(type, a); | |
| 203 LayoutUnit bStart = start(type, b); | |
| 204 LayoutUnit aEnd = end(type, a); | |
| 205 LayoutUnit bEnd = end(type, b); | |
| 206 | |
| 207 // Picture of the partially aligned logic: | |
| 208 // | |
| 209 // Horizontal Vertical | |
| 210 // ******************************** | |
| 211 // * _ * _ _ _ * | |
| 212 // * |_| * |_|_|_| * | |
| 213 // * |_|.... _ * . . * | |
| 214 // * |_| |_| * . . * | |
| 215 // * |_|....|_| * ._._ _ * | |
| 216 // * |_| * |_|_|_| * | |
| 217 // * |_| * * | |
| 218 // * * * | |
| 219 // ******************************** | |
| 220 // | |
| 221 // ... and variants of the above cases. | |
| 222 return (bStart >= aStart && bStart <= aEnd) | |
| 223 || (bEnd >= aStart && bEnd <= aEnd); | |
| 224 } | |
| 225 | |
| 226 static bool areRectsMoreThanFullScreenApart(WebFocusType type, const LayoutRect&
curRect, const LayoutRect& targetRect, const LayoutSize& viewSize) | |
| 227 { | |
| 228 ASSERT(isRectInDirection(type, curRect, targetRect)); | |
| 229 | |
| 230 switch (type) { | |
| 231 case WebFocusTypeLeft: | |
| 232 return curRect.x() - targetRect.maxX() > viewSize.width(); | |
| 233 case WebFocusTypeRight: | |
| 234 return targetRect.x() - curRect.maxX() > viewSize.width(); | |
| 235 case WebFocusTypeUp: | |
| 236 return curRect.y() - targetRect.maxY() > viewSize.height(); | |
| 237 case WebFocusTypeDown: | |
| 238 return targetRect.y() - curRect.maxY() > viewSize.height(); | |
| 239 default: | |
| 240 ASSERT_NOT_REACHED(); | |
| 241 return true; | |
| 242 } | |
| 243 } | 102 } |
| 244 | 103 |
| 245 // Return true if rect |a| is below |b|. False otherwise. | 104 // Return true if rect |a| is below |b|. False otherwise. |
| 246 // For overlapping rects, |a| is considered to be below |b| | 105 // For overlapping rects, |a| is considered to be below |b| |
| 247 // if both edges of |a| are below the respective ones of |b| | 106 // if both edges of |a| are below the respective ones of |b| |
| 248 static inline bool below(const LayoutRect& a, const LayoutRect& b) | 107 static inline bool below(const LayoutRect& a, const LayoutRect& b) |
| 249 { | 108 { |
| 250 return a.y() >= b.maxY() | 109 return a.y() >= b.maxY() |
| 251 || (a.y() >= b.y() && a.maxY() > b.maxY() && a.x() < b.maxX() && a.maxX(
) > b.x()); | 110 || (a.y() >= b.y() && a.maxY() > b.maxY() && a.x() < b.maxX() && a.maxX(
) > b.x()); |
| 252 } | 111 } |
| (...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 637 return false; | 496 return false; |
| 638 | 497 |
| 639 return true; | 498 return true; |
| 640 } | 499 } |
| 641 | 500 |
| 642 void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus
Candidate& candidate) | 501 void distanceDataForNode(WebFocusType type, const FocusCandidate& current, Focus
Candidate& candidate) |
| 643 { | 502 { |
| 644 if (areElementsOnSameLine(current, candidate)) { | 503 if (areElementsOnSameLine(current, candidate)) { |
| 645 if ((type == WebFocusTypeUp && current.rect.y() > candidate.rect.y()) ||
(type == WebFocusTypeDown && candidate.rect.y() > current.rect.y())) { | 504 if ((type == WebFocusTypeUp && current.rect.y() > candidate.rect.y()) ||
(type == WebFocusTypeDown && candidate.rect.y() > current.rect.y())) { |
| 646 candidate.distance = 0; | 505 candidate.distance = 0; |
| 647 candidate.alignment = Full; | |
| 648 return; | 506 return; |
| 649 } | 507 } |
| 650 } | 508 } |
| 651 | 509 |
| 652 LayoutRect nodeRect = candidate.rect; | 510 LayoutRect nodeRect = candidate.rect; |
| 653 LayoutRect currentRect = current.rect; | 511 LayoutRect currentRect = current.rect; |
| 654 deflateIfOverlapped(currentRect, nodeRect); | 512 deflateIfOverlapped(currentRect, nodeRect); |
| 655 | 513 |
| 656 if (!isRectInDirection(type, currentRect, nodeRect)) | 514 if (!isRectInDirection(type, currentRect, nodeRect)) |
| 657 return; | 515 return; |
| 658 | 516 |
| 659 LayoutPoint exitPoint; | 517 LayoutPoint exitPoint; |
| 660 LayoutPoint entryPoint; | 518 LayoutPoint entryPoint; |
| 661 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry
Point); | 519 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry
Point); |
| 662 | 520 |
| 663 LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); | 521 LayoutUnit xAxis = (exitPoint.x() - entryPoint.x()).abs(); |
| 664 LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); | 522 LayoutUnit yAxis = (exitPoint.y() - entryPoint.y()).abs(); |
| 665 | 523 |
| 666 LayoutUnit navigationAxisDistance; | 524 LayoutUnit navigationAxisDistance; |
| 667 LayoutUnit orthogonalAxisDistance; | 525 LayoutUnit weightedOrthogonalAxisDistance; |
| 526 |
| 527 // Bias and weights are put to the orthogonal axis distance calculation |
| 528 // so aligned candidates would have advantage over partially-aligned ones |
| 529 // and then over not-aligned candidates. The bias is given to not-aligned |
| 530 // candidates with respect to size of the current rect. The weight for |
| 531 // left/right direction is given a higher value to allow navigation on |
| 532 // common horizonally-aligned elements. The hardcoded values are based on |
| 533 // tests and experiments. |
| 534 const int orthogonalWeightForLeftRight = 30; |
| 535 const int orthogonalWeightForUpDown = 2; |
| 536 int orthogonalBias = 0; |
| 668 | 537 |
| 669 switch (type) { | 538 switch (type) { |
| 670 case WebFocusTypeLeft: | 539 case WebFocusTypeLeft: |
| 671 case WebFocusTypeRight: | 540 case WebFocusTypeRight: |
| 672 navigationAxisDistance = xAxis.abs(); | 541 navigationAxisDistance = xAxis; |
| 673 orthogonalAxisDistance = yAxis.abs(); | 542 if (!rectsIntersectOnOrthogonalAxis(type, currentRect, nodeRect)) |
| 543 orthogonalBias = currentRect.height() / 2; |
| 544 weightedOrthogonalAxisDistance = (yAxis + orthogonalBias) * orthogonalWe
ightForLeftRight; |
| 674 break; | 545 break; |
| 675 case WebFocusTypeUp: | 546 case WebFocusTypeUp: |
| 676 case WebFocusTypeDown: | 547 case WebFocusTypeDown: |
| 677 navigationAxisDistance = yAxis.abs(); | 548 navigationAxisDistance = yAxis; |
| 678 orthogonalAxisDistance = xAxis.abs(); | 549 if (!rectsIntersectOnOrthogonalAxis(type, currentRect, nodeRect)) |
| 550 orthogonalBias = currentRect.width() / 2; |
| 551 weightedOrthogonalAxisDistance = (xAxis + orthogonalBias) * orthogonalWe
ightForUpDown; |
| 679 break; | 552 break; |
| 680 default: | 553 default: |
| 681 ASSERT_NOT_REACHED(); | 554 ASSERT_NOT_REACHED(); |
| 682 return; | 555 return; |
| 683 } | 556 } |
| 684 | 557 |
| 685 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); | 558 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); |
| 686 LayoutRect intersectionRect = intersection(currentRect, nodeRect); | 559 LayoutRect intersectionRect = intersection(currentRect, nodeRect); |
| 687 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo
uble(); | 560 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo
uble(); |
| 688 | 561 |
| 689 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin
g | 562 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin
g |
| 690 candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance+ o
rthogonalAxisDistance * 2 - sqrt(overlap); | 563 candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance +
weightedOrthogonalAxisDistance - sqrt(overlap); |
| 691 | |
| 692 LayoutSize viewSize = LayoutSize(candidate.visibleNode->document().page()->d
eprecatedLocalMainFrame()->view()->visibleContentRect().size()); | |
| 693 candidate.alignment = alignmentForRects(type, currentRect, nodeRect, viewSiz
e); | |
| 694 } | 564 } |
| 695 | 565 |
| 696 bool canBeScrolledIntoView(WebFocusType type, const FocusCandidate& candidate) | 566 bool canBeScrolledIntoView(WebFocusType type, const FocusCandidate& candidate) |
| 697 { | 567 { |
| 698 ASSERT(candidate.visibleNode && candidate.isOffscreen); | 568 ASSERT(candidate.visibleNode && candidate.isOffscreen); |
| 699 LayoutRect candidateRect = candidate.rect; | 569 LayoutRect candidateRect = candidate.rect; |
| 700 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par
entNode = parentNode->parentNode()) { | 570 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par
entNode = parentNode->parentNode()) { |
| 701 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); | 571 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); |
| 702 if (!candidateRect.intersects(parentRect)) { | 572 if (!candidateRect.intersects(parentRect)) { |
| 703 if (((type == WebFocusTypeLeft || type == WebFocusTypeRight) && pare
ntNode->renderer()->style()->overflowX() == OHIDDEN) | 573 if (((type == WebFocusTypeLeft || type == WebFocusTypeRight) && pare
ntNode->renderer()->style()->overflowX() == OHIDDEN) |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 747 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar
ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1); | 617 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar
ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1); |
| 748 return rect; | 618 return rect; |
| 749 } | 619 } |
| 750 | 620 |
| 751 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) | 621 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) |
| 752 { | 622 { |
| 753 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v
isibleNode) : nullptr; | 623 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v
isibleNode) : nullptr; |
| 754 }; | 624 }; |
| 755 | 625 |
| 756 } // namespace blink | 626 } // namespace blink |
| OLD | NEW |