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 |