2 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (C) 2009 Antonio Gomes <tonikitoo@webkit.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "core/page/SpatialNavigation.h"
32 #include "core/HTMLNames.h"
33 #include "core/frame/FrameView.h"
34 #include "core/frame/LocalFrame.h"
35 #include "core/frame/Settings.h"
36 #include "core/html/HTMLAreaElement.h"
37 #include "core/html/HTMLFrameOwnerElement.h"
38 #include "core/html/HTMLImageElement.h"
39 #include "core/page/FrameTree.h"
40 #include "core/page/Page.h"
41 #include "core/rendering/RenderLayer.h"
42 #include "platform/geometry/IntRect.h"
46 using namespace HTMLNames;
48 static RectsAlignment alignmentForRects(FocusType, const LayoutRect&, const LayoutRect&, const LayoutSize& viewSize);
49 static bool areRectsFullyAligned(FocusType, const LayoutRect&, const LayoutRect&);
50 static bool areRectsPartiallyAligned(FocusType, const LayoutRect&, const LayoutRect&);
51 static bool areRectsMoreThanFullScreenApart(FocusType, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize);
52 static bool isRectInDirection(FocusType, const LayoutRect&, const LayoutRect&);
53 static void deflateIfOverlapped(LayoutRect&, LayoutRect&);
54 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const LayoutRect&);
55 static bool isScrollableNode(const Node*);
57 FocusCandidate::FocusCandidate(Node* node, FocusType type)
58 : visibleNode(nullptr)
59 , focusableNode(nullptr)
60 , enclosingScrollableBox(nullptr)
61 , distance(maxDistance())
64 , isOffscreenAfterScrolling(true)
67 ASSERT(node->isElementNode());
69 if (isHTMLAreaElement(*node)) {
70 HTMLAreaElement& area = toHTMLAreaElement(*node);
71 HTMLImageElement* image = area.imageElement();
72 if (!image || !image->renderer())
76 rect = virtualRectForAreaElementAndDirection(area, type);
78 if (!node->renderer())
82 rect = nodeRectInAbsoluteCoordinates(node, true /* ignore border */);
86 isOffscreen = hasOffscreenRect(visibleNode);
87 isOffscreenAfterScrolling = hasOffscreenRect(visibleNode, type);
90 bool isSpatialNavigationEnabled(const LocalFrame* frame)
92 return (frame && frame->settings() && frame->settings()->spatialNavigationEnabled());
95 static RectsAlignment alignmentForRects(FocusType type, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize)
97 // If we found a node in full alignment, but it is too far away, ignore it.
98 if (areRectsMoreThanFullScreenApart(type, curRect, targetRect, viewSize))
101 if (areRectsFullyAligned(type, curRect, targetRect))
104 if (areRectsPartiallyAligned(type, curRect, targetRect))
110 static inline bool isHorizontalMove(FocusType type)
112 return type == FocusTypeLeft || type == FocusTypeRight;
115 static inline LayoutUnit start(FocusType type, const LayoutRect& rect)
117 return isHorizontalMove(type) ? rect.y() : rect.x();
120 static inline LayoutUnit middle(FocusType type, const LayoutRect& rect)
122 LayoutPoint center(rect.center());
123 return isHorizontalMove(type) ? center.y(): center.x();
126 static inline LayoutUnit end(FocusType type, const LayoutRect& rect)
128 return isHorizontalMove(type) ? rect.maxY() : rect.maxX();
131 // This method checks if rects |a| and |b| are fully aligned either vertically or
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 changing
136 // * a = Current focused node's rect.
137 // * b = Focus candidate node's rect.
138 static bool areRectsFullyAligned(FocusType type, const LayoutRect& a, const LayoutRect& b)
140 LayoutUnit aStart, bStart, aEnd, bEnd;
160 ASSERT_NOT_REACHED();
167 aStart = start(type, a);
168 bStart = start(type, b);
170 LayoutUnit aMiddle = middle(type, a);
171 LayoutUnit bMiddle = middle(type, b);
176 // Picture of the totally aligned logic:
178 // Horizontal Vertical Horizontal Vertical
179 // **************************** *****************************
180 // * _ * _ _ _ _ * * _ * _ _ *
181 // * |_| _ * |_|_|_|_| * * _ |_| * |_|_| *
182 // * |_|....|_| * . * * |_|....|_| * . *
183 // * |_| |_| (1) . * * |_| |_| (2) . *
184 // * |_| * _._ * * |_| * _ _._ _ *
185 // * * |_|_| * * * |_|_|_|_| *
187 // **************************** *****************************
189 return (bMiddle >= aStart && bMiddle <= aEnd) // (1)
190 || (aMiddle >= bStart && aMiddle <= bEnd); // (2)
193 // This method checks if rects |a| and |b| are partially aligned either vertically or
194 // horizontally. In general, rects whose either of edges falls between the top or
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)
202 LayoutUnit aStart = start(type, a);
203 LayoutUnit bStart = start(type, b);
204 LayoutUnit aEnd = end(type, a);
205 LayoutUnit bEnd = end(type, b);
207 // Picture of the partially aligned logic:
209 // Horizontal Vertical
210 // ********************************
213 // * |_|.... _ * . . *
215 // * |_|....|_| * ._._ _ *
219 // ********************************
221 // ... and variants of the above cases.
222 return (bStart >= aStart && bStart <= aEnd)
223 || (bEnd >= aStart && bEnd <= aEnd);
226 static bool areRectsMoreThanFullScreenApart(FocusType type, const LayoutRect& curRect, const LayoutRect& targetRect, const LayoutSize& viewSize)
228 ASSERT(isRectInDirection(type, curRect, targetRect));
232 return curRect.x() - targetRect.maxX() > viewSize.width();
234 return targetRect.x() - curRect.maxX() > viewSize.width();
236 return curRect.y() - targetRect.maxY() > viewSize.height();
238 return targetRect.y() - curRect.maxY() > viewSize.height();
240 ASSERT_NOT_REACHED();
245 // Return true if rect |a| is below |b|. False otherwise.
246 // For overlapping rects, |a| is considered to be below |b|
247 // if both edges of |a| are below the respective ones of |b|
248 static inline bool below(const LayoutRect& a, const LayoutRect& b)
250 return a.y() >= b.maxY()
251 || (a.y() >= b.y() && a.maxY() > b.maxY());
254 // Return true if rect |a| is on the right of |b|. False otherwise.
255 // For overlapping rects, |a| is considered to be on the right of |b|
256 // if both edges of |a| are on the right of the respective ones of |b|
257 static inline bool rightOf(const LayoutRect& a, const LayoutRect& b)
259 return a.x() >= b.maxX()
260 || (a.x() >= b.x() && a.maxX() > b.maxX());
263 static bool isRectInDirection(FocusType type, const LayoutRect& curRect, const LayoutRect& targetRect)
267 return rightOf(curRect, targetRect);
269 return rightOf(targetRect, curRect);
271 return below(curRect, targetRect);
273 return below(targetRect, curRect);
275 ASSERT_NOT_REACHED();
280 // Checks if |node| is offscreen the visible area (viewport) of its container
281 // document. In case it is, one can scroll in direction or take any different
282 // desired action later on.
283 bool hasOffscreenRect(Node* node, FocusType type)
285 // Get the FrameView in which |node| is (which means the current viewport if |node|
286 // is not in an inner document), so we can check if its content rect is visible
287 // before we actually move the focus to it.
288 FrameView* frameView = node->document().view();
292 ASSERT(!frameView->needsLayout());
294 LayoutRect containerViewportRect = frameView->visibleContentRect();
295 // We want to select a node if it is currently off screen, but will be
296 // exposed after we scroll. Adjust the viewport to post-scrolling position.
297 // If the container has overflow:hidden, we cannot scroll, so we do not pass direction
298 // and we do not adjust for scrolling.
301 containerViewportRect.setX(containerViewportRect.x() - ScrollableArea::pixelsPerLineStep());
302 containerViewportRect.setWidth(containerViewportRect.width() + ScrollableArea::pixelsPerLineStep());
305 containerViewportRect.setWidth(containerViewportRect.width() + ScrollableArea::pixelsPerLineStep());
308 containerViewportRect.setY(containerViewportRect.y() - ScrollableArea::pixelsPerLineStep());
309 containerViewportRect.setHeight(containerViewportRect.height() + ScrollableArea::pixelsPerLineStep());
312 containerViewportRect.setHeight(containerViewportRect.height() + ScrollableArea::pixelsPerLineStep());
318 RenderObject* render = node->renderer();
322 LayoutRect rect(render->absoluteClippedOverflowRect());
326 return !containerViewportRect.intersects(rect);
329 bool scrollInDirection(LocalFrame* frame, FocusType type)
333 if (frame && canScrollInDirection(frame->document(), type)) {
338 dx = - ScrollableArea::pixelsPerLineStep();
341 dx = ScrollableArea::pixelsPerLineStep();
344 dy = - ScrollableArea::pixelsPerLineStep();
347 dy = ScrollableArea::pixelsPerLineStep();
350 ASSERT_NOT_REACHED();
354 frame->view()->scrollBy(IntSize(dx, dy));
360 bool scrollInDirection(Node* container, FocusType type)
363 if (container->isDocumentNode())
364 return scrollInDirection(toDocument(container)->frame(), type);
366 if (!container->renderBox())
369 if (canScrollInDirection(container, type)) {
374 dx = - std::min<LayoutUnit>(ScrollableArea::pixelsPerLineStep(), container->renderBox()->scrollLeft());
377 ASSERT(container->renderBox()->scrollWidth() > (container->renderBox()->scrollLeft() + container->renderBox()->clientWidth()));
378 dx = std::min<LayoutUnit>(ScrollableArea::pixelsPerLineStep(), container->renderBox()->scrollWidth() - (container->renderBox()->scrollLeft() + container->renderBox()->clientWidth()));
381 dy = - std::min<LayoutUnit>(ScrollableArea::pixelsPerLineStep(), container->renderBox()->scrollTop());
384 ASSERT(container->renderBox()->scrollHeight() - (container->renderBox()->scrollTop() + container->renderBox()->clientHeight()));
385 dy = std::min<LayoutUnit>(ScrollableArea::pixelsPerLineStep(), container->renderBox()->scrollHeight() - (container->renderBox()->scrollTop() + container->renderBox()->clientHeight()));
388 ASSERT_NOT_REACHED();
392 container->renderBox()->scrollByRecursively(IntSize(dx, dy));
399 static void deflateIfOverlapped(LayoutRect& a, LayoutRect& b)
401 if (!a.intersects(b) || a.contains(b) || b.contains(a))
404 LayoutUnit deflateFactor = -fudgeFactor();
406 // Avoid negative width or height values.
407 if ((a.width() + 2 * deflateFactor > 0) && (a.height() + 2 * deflateFactor > 0))
408 a.inflate(deflateFactor);
410 if ((b.width() + 2 * deflateFactor > 0) && (b.height() + 2 * deflateFactor > 0))
411 b.inflate(deflateFactor);
414 bool isScrollableNode(const Node* node)
416 ASSERT(!node->isDocumentNode());
421 if (RenderObject* renderer = node->renderer())
422 return renderer->isBox() && toRenderBox(renderer)->canBeScrolledAndHasScrollableArea() && node->hasChildren();
427 Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusType type, Node* node)
432 // FIXME: Spatial navigation is broken for OOPI.
433 if (parent->isDocumentNode())
434 parent = toDocument(parent)->frame()->deprecatedLocalOwner();
436 parent = parent->parentOrShadowHostNode();
437 } while (parent && !canScrollInDirection(parent, type) && !parent->isDocumentNode());
442 bool canScrollInDirection(const Node* container, FocusType type)
445 if (container->isDocumentNode())
446 return canScrollInDirection(toDocument(container)->frame(), type);
448 if (!isScrollableNode(container))
453 return (container->renderer()->style()->overflowX() != OHIDDEN && container->renderBox()->scrollLeft() > 0);
455 return (container->renderer()->style()->overflowY() != OHIDDEN && container->renderBox()->scrollTop() > 0);
457 return (container->renderer()->style()->overflowX() != OHIDDEN && container->renderBox()->scrollLeft() + container->renderBox()->clientWidth() < container->renderBox()->scrollWidth());
459 return (container->renderer()->style()->overflowY() != OHIDDEN && container->renderBox()->scrollTop() + container->renderBox()->clientHeight() < container->renderBox()->scrollHeight());
461 ASSERT_NOT_REACHED();
466 bool canScrollInDirection(const LocalFrame* frame, FocusType type)
470 ScrollbarMode verticalMode;
471 ScrollbarMode horizontalMode;
472 frame->view()->calculateScrollbarModesForLayoutAndSetViewportRenderer(horizontalMode, verticalMode);
473 if ((type == FocusTypeLeft || type == FocusTypeRight) && ScrollbarAlwaysOff == horizontalMode)
475 if ((type == FocusTypeUp || type == FocusTypeDown) && ScrollbarAlwaysOff == verticalMode)
477 LayoutSize size = frame->view()->contentsSize();
478 LayoutSize offset = frame->view()->scrollOffset();
479 LayoutRect rect = frame->view()->visibleContentRect(IncludeScrollbars);
483 return offset.width() > 0;
485 return offset.height() > 0;
487 return rect.width() + offset.width() < size.width();
489 return rect.height() + offset.height() < size.height();
491 ASSERT_NOT_REACHED();
496 static LayoutRect rectToAbsoluteCoordinates(LocalFrame* initialFrame, const LayoutRect& initialRect)
498 LayoutRect rect = initialRect;
499 for (Frame* frame = initialFrame; frame; frame = frame->tree().parent()) {
500 if (!frame->isLocalFrame())
502 // FIXME: Spatial navigation is broken for OOPI.
503 Element* element = frame->deprecatedLocalOwner();
505 for (; element; element = element->offsetParent())
506 rect.move(element->offsetLeft(), element->offsetTop());
507 rect.move((-toLocalFrame(frame)->view()->scrollOffset()));
513 LayoutRect nodeRectInAbsoluteCoordinates(Node* node, bool ignoreBorder)
515 ASSERT(node && node->renderer() && !node->document().view()->needsLayout());
517 if (node->isDocumentNode())
518 return frameRectInAbsoluteCoordinates(toDocument(node)->frame());
519 LayoutRect rect = rectToAbsoluteCoordinates(node->document().frame(), node->boundingBox());
521 // For authors that use border instead of outline in their CSS, we compensate by ignoring the border when calculating
522 // the rect of the focused element.
524 rect.move(node->renderer()->style()->borderLeftWidth(), node->renderer()->style()->borderTopWidth());
525 rect.setWidth(rect.width() - node->renderer()->style()->borderLeftWidth() - node->renderer()->style()->borderRightWidth());
526 rect.setHeight(rect.height() - node->renderer()->style()->borderTopWidth() - node->renderer()->style()->borderBottomWidth());
531 LayoutRect frameRectInAbsoluteCoordinates(LocalFrame* frame)
533 return rectToAbsoluteCoordinates(frame, frame->view()->visibleContentRect());
536 // This method calculates the exitPoint from the startingRect and the entryPoint into the candidate rect.
537 // The line between those 2 points is the closest distance between the 2 rects.
538 // Takes care of overlapping rects, defining points so that the distance between them
539 // is zero where necessary
540 void entryAndExitPointsForDirection(FocusType type, const LayoutRect& startingRect, const LayoutRect& potentialRect, LayoutPoint& exitPoint, LayoutPoint& entryPoint)
544 exitPoint.setX(startingRect.x());
545 if (potentialRect.maxX() < startingRect.x())
546 entryPoint.setX(potentialRect.maxX());
548 entryPoint.setX(startingRect.x());
551 exitPoint.setY(startingRect.y());
552 if (potentialRect.maxY() < startingRect.y())
553 entryPoint.setY(potentialRect.maxY());
555 entryPoint.setY(startingRect.y());
558 exitPoint.setX(startingRect.maxX());
559 if (potentialRect.x() > startingRect.maxX())
560 entryPoint.setX(potentialRect.x());
562 entryPoint.setX(startingRect.maxX());
565 exitPoint.setY(startingRect.maxY());
566 if (potentialRect.y() > startingRect.maxY())
567 entryPoint.setY(potentialRect.y());
569 entryPoint.setY(startingRect.maxY());
572 ASSERT_NOT_REACHED();
578 if (below(startingRect, potentialRect)) {
579 exitPoint.setY(startingRect.y());
580 if (potentialRect.maxY() < startingRect.y())
581 entryPoint.setY(potentialRect.maxY());
583 entryPoint.setY(startingRect.y());
584 } else if (below(potentialRect, startingRect)) {
585 exitPoint.setY(startingRect.maxY());
586 if (potentialRect.y() > startingRect.maxY())
587 entryPoint.setY(potentialRect.y());
589 entryPoint.setY(startingRect.maxY());
591 exitPoint.setY(max(startingRect.y(), potentialRect.y()));
592 entryPoint.setY(exitPoint.y());
597 if (rightOf(startingRect, potentialRect)) {
598 exitPoint.setX(startingRect.x());
599 if (potentialRect.maxX() < startingRect.x())
600 entryPoint.setX(potentialRect.maxX());
602 entryPoint.setX(startingRect.x());
603 } else if (rightOf(potentialRect, startingRect)) {
604 exitPoint.setX(startingRect.maxX());
605 if (potentialRect.x() > startingRect.maxX())
606 entryPoint.setX(potentialRect.x());
608 entryPoint.setX(startingRect.maxX());
610 exitPoint.setX(max(startingRect.x(), potentialRect.x()));
611 entryPoint.setX(exitPoint.x());
615 ASSERT_NOT_REACHED();
619 bool areElementsOnSameLine(const FocusCandidate& firstCandidate, const FocusCandidate& secondCandidate)
621 if (firstCandidate.isNull() || secondCandidate.isNull())
624 if (!firstCandidate.visibleNode->renderer() || !secondCandidate.visibleNode->renderer())
627 if (!firstCandidate.rect.intersects(secondCandidate.rect))
630 if (isHTMLAreaElement(*firstCandidate.focusableNode) || isHTMLAreaElement(*secondCandidate.focusableNode))
633 if (!firstCandidate.visibleNode->renderer()->isRenderInline() || !secondCandidate.visibleNode->renderer()->isRenderInline())
636 if (firstCandidate.visibleNode->renderer()->containingBlock() != secondCandidate.visibleNode->renderer()->containingBlock())
642 void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCandidate& candidate)
644 if (areElementsOnSameLine(current, candidate)) {
645 if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (type == FocusTypeDown && candidate.rect.y() > current.rect.y())) {
646 candidate.distance = 0;
647 candidate.alignment = Full;
652 LayoutRect nodeRect = candidate.rect;
653 LayoutRect currentRect = current.rect;
654 deflateIfOverlapped(currentRect, nodeRect);
656 if (!isRectInDirection(type, currentRect, nodeRect))
659 LayoutPoint exitPoint;
660 LayoutPoint entryPoint;
661 entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entryPoint);
663 LayoutUnit xAxis = exitPoint.x() - entryPoint.x();
664 LayoutUnit yAxis = exitPoint.y() - entryPoint.y();
666 LayoutUnit navigationAxisDistance;
667 LayoutUnit orthogonalAxisDistance;
672 navigationAxisDistance = xAxis.abs();
673 orthogonalAxisDistance = yAxis.abs();
677 navigationAxisDistance = yAxis.abs();
678 orthogonalAxisDistance = xAxis.abs();
681 ASSERT_NOT_REACHED();
685 double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble();
686 LayoutRect intersectionRect = intersection(currentRect, nodeRect);
687 double overlap = (intersectionRect.width() * intersectionRect.height()).toDouble();
689 // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handling
690 candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance+ orthogonalAxisDistance * 2 - sqrt(overlap);
692 LayoutSize viewSize = candidate.visibleNode->document().page()->deprecatedLocalMainFrame()->view()->visibleContentRect().size();
693 candidate.alignment = alignmentForRects(type, currentRect, nodeRect, viewSize);
696 bool canBeScrolledIntoView(FocusType type, const FocusCandidate& candidate)
698 ASSERT(candidate.visibleNode && candidate.isOffscreen);
699 LayoutRect candidateRect = candidate.rect;
700 for (Node* parentNode = candidate.visibleNode->parentNode(); parentNode; parentNode = parentNode->parentNode()) {
701 LayoutRect parentRect = nodeRectInAbsoluteCoordinates(parentNode);
702 if (!candidateRect.intersects(parentRect)) {
703 if (((type == FocusTypeLeft || type == FocusTypeRight) && parentNode->renderer()->style()->overflowX() == OHIDDEN)
704 || ((type == FocusTypeUp || type == FocusTypeDown) && parentNode->renderer()->style()->overflowY() == OHIDDEN))
707 if (parentNode == candidate.enclosingScrollableBox)
708 return canScrollInDirection(parentNode, type);
713 // The starting rect is the rect of the focused node, in document coordinates.
714 // Compose a virtual starting rect if there is no focused node or if it is off screen.
715 // The virtual rect is the edge of the container or frame. We select which
716 // edge depending on the direction of the navigation.
717 LayoutRect virtualRectForDirection(FocusType type, const LayoutRect& startingRect, LayoutUnit width)
719 LayoutRect virtualStartingRect = startingRect;
722 virtualStartingRect.setX(virtualStartingRect.maxX() - width);
723 virtualStartingRect.setWidth(width);
726 virtualStartingRect.setY(virtualStartingRect.maxY() - width);
727 virtualStartingRect.setHeight(width);
730 virtualStartingRect.setWidth(width);
733 virtualStartingRect.setHeight(width);
736 ASSERT_NOT_REACHED();
739 return virtualStartingRect;
742 LayoutRect virtualRectForAreaElementAndDirection(HTMLAreaElement& area, FocusType type)
744 ASSERT(area.imageElement());
745 // Area elements tend to overlap more than other focusable elements. We flatten the rect of the area elements
746 // to minimize the effect of overlapping areas.
747 LayoutRect rect = virtualRectForDirection(type, rectToAbsoluteCoordinates(area.document().frame(), area.computeRect(area.imageElement()->renderer())), 1);
751 HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate)
753 return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.visibleNode) : nullptr;