Chromium Code Reviews| 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(FocusType, const LayoutRect&, const Layo utRect&, const LayoutSize& viewSize); | |
| 49 static bool areRectsFullyAligned(FocusType, const LayoutRect&, const LayoutRect& ); | |
| 50 static bool areRectsPartiallyAligned(FocusType, const LayoutRect&, const LayoutR ect&); | |
| 51 static bool areRectsMoreThanFullScreenApart(FocusType, const LayoutRect& curRect , const LayoutRect& targetRect, const LayoutSize& viewSize); | |
| 52 static bool isRectInDirection(FocusType, const LayoutRect&, const LayoutRect&); | 48 static bool isRectInDirection(FocusType, const LayoutRect&, const LayoutRect&); |
| 53 static void deflateIfOverlapped(LayoutRect&, LayoutRect&); | 49 static void deflateIfOverlapped(LayoutRect&, LayoutRect&); |
| 54 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo utRect&); | 50 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const Layo utRect&); |
| 55 static bool isScrollableNode(const Node*); | 51 static bool isScrollableNode(const Node*); |
| 56 | 52 |
| 57 FocusCandidate::FocusCandidate(Node* node, FocusType type) | 53 FocusCandidate::FocusCandidate(Node* node, FocusType type) |
| 58 : visibleNode(nullptr) | 54 : visibleNode(nullptr) |
| 59 , focusableNode(nullptr) | 55 , focusableNode(nullptr) |
| 60 , enclosingScrollableBox(nullptr) | 56 , enclosingScrollableBox(nullptr) |
| 61 , distance(maxDistance()) | 57 , distance(maxDistance()) |
| 62 , alignment(None) | |
| 63 , isOffscreen(true) | 58 , isOffscreen(true) |
| 64 , isOffscreenAfterScrolling(true) | 59 , isOffscreenAfterScrolling(true) |
| 65 { | 60 { |
| 66 ASSERT(node); | 61 ASSERT(node); |
| 67 ASSERT(node->isElementNode()); | 62 ASSERT(node->isElementNode()); |
| 68 | 63 |
| 69 if (isHTMLAreaElement(*node)) { | 64 if (isHTMLAreaElement(*node)) { |
| 70 HTMLAreaElement& area = toHTMLAreaElement(*node); | 65 HTMLAreaElement& area = toHTMLAreaElement(*node); |
| 71 HTMLImageElement* image = area.imageElement(); | 66 HTMLImageElement* image = area.imageElement(); |
| 72 if (!image || !image->renderer()) | 67 if (!image || !image->renderer()) |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 85 focusableNode = node; | 80 focusableNode = node; |
| 86 isOffscreen = hasOffscreenRect(visibleNode); | 81 isOffscreen = hasOffscreenRect(visibleNode); |
| 87 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type); | 82 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type); |
| 88 } | 83 } |
| 89 | 84 |
| 90 bool isSpatialNavigationEnabled(const LocalFrame* frame) | 85 bool isSpatialNavigationEnabled(const LocalFrame* frame) |
| 91 { | 86 { |
| 92 return (frame && frame->settings() && frame->settings()->spatialNavigationEn abled()); | 87 return (frame && frame->settings() && frame->settings()->spatialNavigationEn abled()); |
| 93 } | 88 } |
| 94 | 89 |
| 95 static RectsAlignment alignmentForRects(FocusType type, const LayoutRect& curRec t, const LayoutRect& targetRect, const LayoutSize& viewSize) | 90 static bool areRectsNotAligned(FocusType type, const LayoutRect& a, const Layout Rect& b) |
|
fs
2015/01/12 12:07:23
Not the most obvious name IMHO. Maybe rectsInterse
c.shu
2015/01/12 18:03:38
will do.
| |
| 96 { | 91 { |
| 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(FocusType type) | |
| 111 { | |
| 112 return type == FocusTypeLeft || type == FocusTypeRight; | |
| 113 } | |
| 114 | |
| 115 static inline LayoutUnit start(FocusType type, const LayoutRect& rect) | |
| 116 { | |
| 117 return isHorizontalMove(type) ? rect.y() : rect.x(); | |
| 118 } | |
| 119 | |
| 120 static inline LayoutUnit middle(FocusType 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(FocusType 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(FocusType type, const LayoutRect& a, const Layo utRect& b) | |
| 139 { | |
| 140 LayoutUnit aStart, bStart, aEnd, bEnd; | |
| 141 | |
| 142 switch (type) { | 92 switch (type) { |
| 143 case FocusTypeLeft: | 93 case FocusTypeLeft: |
| 144 aStart = a.x(); | |
| 145 bEnd = b.x(); | |
| 146 break; | |
| 147 case FocusTypeRight: | 94 case FocusTypeRight: |
| 148 aStart = b.x(); | 95 return a.maxY() <= b.y() || a.y() >= b.maxY(); |
| 149 bEnd = a.x(); | |
| 150 break; | |
| 151 case FocusTypeUp: | 96 case FocusTypeUp: |
| 152 aStart = a.y(); | |
| 153 bEnd = b.y(); | |
| 154 break; | |
| 155 case FocusTypeDown: | 97 case FocusTypeDown: |
| 156 aStart = b.y(); | 98 return a.maxX() <= b.x() || a.x() >= b.maxX(); |
| 157 bEnd = a.y(); | |
| 158 break; | |
| 159 default: | |
| 160 ASSERT_NOT_REACHED(); | |
| 161 return false; | |
| 162 } | |
| 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(FocusType type, const LayoutRect& a, const 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(FocusType type, const LayoutRect& cu rRect, const LayoutRect& targetRect, const LayoutSize& viewSize) | |
| 227 { | |
| 228 ASSERT(isRectInDirection(type, curRect, targetRect)); | |
| 229 | |
| 230 switch (type) { | |
| 231 case FocusTypeLeft: | |
| 232 return curRect.x() - targetRect.maxX() > viewSize.width(); | |
| 233 case FocusTypeRight: | |
| 234 return targetRect.x() - curRect.maxX() > viewSize.width(); | |
| 235 case FocusTypeUp: | |
| 236 return curRect.y() - targetRect.maxY() > viewSize.height(); | |
| 237 case FocusTypeDown: | |
| 238 return targetRect.y() - curRect.maxY() > viewSize.height(); | |
| 239 default: | 99 default: |
| 240 ASSERT_NOT_REACHED(); | 100 ASSERT_NOT_REACHED(); |
| 241 return true; | 101 return true; |
| 242 } | 102 } |
| 243 } | 103 } |
| 244 | 104 |
| 245 // Return true if rect |a| is below |b|. False otherwise. | 105 // Return true if rect |a| is below |b|. False otherwise. |
| 246 // For overlapping rects, |a| is considered to be below |b| | 106 // For overlapping rects, |a| is considered to be below |b| |
| 247 // if both edges of |a| are below the respective ones of |b| | 107 // if both edges of |a| are below the respective ones of |b| |
| 248 static inline bool below(const LayoutRect& a, const LayoutRect& b) | 108 static inline bool below(const LayoutRect& a, const LayoutRect& b) |
| (...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 637 return false; | 497 return false; |
| 638 | 498 |
| 639 return true; | 499 return true; |
| 640 } | 500 } |
| 641 | 501 |
| 642 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCan didate& candidate) | 502 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCan didate& candidate) |
| 643 { | 503 { |
| 644 if (areElementsOnSameLine(current, candidate)) { | 504 if (areElementsOnSameLine(current, candidate)) { |
| 645 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (t ype == FocusTypeDown && candidate.rect.y() > current.rect.y())) { | 505 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (t ype == FocusTypeDown && candidate.rect.y() > current.rect.y())) { |
| 646 candidate.distance = 0; | 506 candidate.distance = 0; |
| 647 candidate.alignment = Full; | |
| 648 return; | 507 return; |
| 649 } | 508 } |
| 650 } | 509 } |
| 651 | 510 |
| 652 LayoutRect nodeRect = candidate.rect; | 511 LayoutRect nodeRect = candidate.rect; |
| 653 LayoutRect currentRect = current.rect; | 512 LayoutRect currentRect = current.rect; |
| 654 deflateIfOverlapped(currentRect, nodeRect); | 513 deflateIfOverlapped(currentRect, nodeRect); |
| 655 | 514 |
| 656 if (!isRectInDirection(type, currentRect, nodeRect)) | 515 if (!isRectInDirection(type, currentRect, nodeRect)) |
| 657 return; | 516 return; |
| 658 | 517 |
| 659 LayoutPoint exitPoint; | 518 LayoutPoint exitPoint; |
| 660 LayoutPoint entryPoint; | 519 LayoutPoint entryPoint; |
| 661 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point); | 520 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entry Point); |
| 662 | 521 |
| 663 LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); | 522 LayoutUnit xAxis = (exitPoint.x() - entryPoint.x()).abs(); |
| 664 LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); | 523 LayoutUnit yAxis = (exitPoint.y() - entryPoint.y()).abs(); |
| 665 | 524 |
| 666 LayoutUnit navigationAxisDistance; | 525 LayoutUnit navigationAxisDistance; |
| 667 LayoutUnit orthogonalAxisDistance; | 526 LayoutUnit weightedOrthogonalAxisDistance; |
| 527 | |
| 528 // Weights are put to the orthogonal axis distance calculation so more align ed candidate | |
| 529 // would have advantage over partially- or not-aligned elements. The weight for left/right | |
| 530 // direction is given a higher value to allow navigation on common horizonal ly-aligned | |
| 531 // elements. Note the hardcoded values are based on tests and experiments. | |
| 532 // The not-aligned node is also given a penalty as the orthogonal distance c ould be 0. | |
| 533 const int orthogonalWeightForLeftRight = 30; | |
| 534 const int orthogonalWeightForUpDown = 2; | |
| 668 | 535 |
| 669 switch (type) { | 536 switch (type) { |
| 670 case FocusTypeLeft: | 537 case FocusTypeLeft: |
| 671 case FocusTypeRight: | 538 case FocusTypeRight: |
| 672 navigationAxisDistance = xAxis.abs(); | 539 navigationAxisDistance = xAxis; |
| 673 orthogonalAxisDistance = yAxis.abs(); | 540 if (areRectsNotAligned(type, currentRect, nodeRect)) |
| 541 yAxis += currentRect.height() / 2; | |
|
fs
2015/01/12 12:07:23
Why mutate the yAxis value here? Shouldn't you jus
c.shu
2015/01/12 18:03:38
The weight has no impact on 0. That's why I have t
fs
2015/01/13 09:26:04
I get the part about 0, but why not for instance (
| |
| 542 weightedOrthogonalAxisDistance = yAxis * orthogonalWeightForLeftRight; | |
| 674 break; | 543 break; |
| 675 case FocusTypeUp: | 544 case FocusTypeUp: |
| 676 case FocusTypeDown: | 545 case FocusTypeDown: |
| 677 navigationAxisDistance = yAxis.abs(); | 546 navigationAxisDistance = yAxis; |
| 678 orthogonalAxisDistance = xAxis.abs(); | 547 if (areRectsNotAligned(type, currentRect, nodeRect)) |
| 548 xAxis += currentRect.width() / 2; | |
| 549 weightedOrthogonalAxisDistance = xAxis * orthogonalWeightForUpDown; | |
| 679 break; | 550 break; |
| 680 default: | 551 default: |
| 681 ASSERT_NOT_REACHED(); | 552 ASSERT_NOT_REACHED(); |
| 682 return; | 553 return; |
| 683 } | 554 } |
| 684 | 555 |
| 685 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); | 556 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); |
| 686 LayoutRect intersectionRect = intersection(currentRect, nodeRect); | 557 LayoutRect intersectionRect = intersection(currentRect, nodeRect); |
| 687 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble(); | 558 double overlap = (intersectionRect.width() * intersectionRect.height()).toDo uble(); |
| 688 | 559 |
| 689 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handlin g | 560 // 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); | 561 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 } | 562 } |
| 695 | 563 |
| 696 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate) | 564 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate) |
| 697 { | 565 { |
| 698 ASSERT(candidate.visibleNode && candidate.isOffscreen); | 566 ASSERT(candidate.visibleNode && candidate.isOffscreen); |
| 699 LayoutRect candidateRect = candidate.rect; | 567 LayoutRect candidateRect = candidate.rect; |
| 700 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) { | 568 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; par entNode = parentNode->parentNode()) { |
| 701 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); | 569 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode); |
| 702 if (!candidateRect.intersects(parentRect)) { | 570 if (!candidateRect.intersects(parentRect)) { |
| 703 if (((type == FocusTypeLeft || type == FocusTypeRight) && parentNode ->renderer()->style()->overflowX() == OHIDDEN) | 571 if (((type == FocusTypeLeft || type == FocusTypeRight) && parentNode ->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); | 615 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(ar ea.document().frame(), area.computeRect(area.imageElement()->renderer())), 1); |
| 748 return rect; | 616 return rect; |
| 749 } | 617 } |
| 750 | 618 |
| 751 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) | 619 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) |
| 752 { | 620 { |
| 753 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr; | 621 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.v isibleNode) : nullptr; |
| 754 }; | 622 }; |
| 755 | 623 |
| 756 } // namespace blink | 624 } // namespace blink |
| OLD | NEW |