Пример #1
0
void FocusController::findFocusCandidateInContainer(Node* container, const LayoutRect& startingRect, FocusDirection direction, KeyboardEvent* event, FocusCandidate& closest)
{
    ASSERT(container);
    Node* focusedNode = (focusedFrame() && focusedFrame()->document()) ? focusedFrame()->document()->focusedNode() : 0;

    Node* node = container->firstChild();
    FocusCandidate current;
    current.rect = startingRect;
    current.focusableNode = focusedNode;
    current.visibleNode = focusedNode;

    for (; node; node = (node->isFrameOwnerElement() || canScrollInDirection(node, direction)) ? NodeTraversal::nextSkippingChildren(node, container) : NodeTraversal::next(node, container)) {
        if (node == focusedNode)
            continue;

        if (!node->isElementNode())
            continue;

        if (!node->isKeyboardFocusable(event) && !node->isFrameOwnerElement() && !canScrollInDirection(node, direction))
            continue;

        FocusCandidate candidate = FocusCandidate(node, direction);
        if (candidate.isNull())
            continue;

        candidate.enclosingScrollableBox = container;
        updateFocusCandidateIfNeeded(direction, current, candidate, closest);
    }
}
Пример #2
0
static void updateFocusCandidateIfCloser(Node* focusedNode, Node* candidate, long long distance, FocusCandidate& closestFocusCandidate)
{
    // Bail out if |distance| is bigger than the current closest candidate.
    if (distance >= closestFocusCandidate.distance)
        return;

    // If |focusedNode| and |candidate| are in the same document AND
    // current |closestFocusCandidadte| is not in an {i}frame that is
    // preferable to get focused.
    if (focusedNode->document() == candidate->document()
        && distance < closestFocusCandidate.parentDistance) {
        closestFocusCandidate.node = candidate;
        closestFocusCandidate.distance = distance;
        closestFocusCandidate.parentDistance = maxDistance();
    } else if (focusedNode->document() != candidate->document()) {
        // If the |focusedNode| is in an inner document and the |candidate| is
        // in a different document, we only consider to change focus if there is
        // not another already good focusable candidate in the same document as
        // |focusedNode|.
        if (!((isInRootDocument(candidate) && !isInRootDocument(focusedNode))
            && focusedNode->document() == closestFocusCandidate.document())) {
            closestFocusCandidate.node = candidate;
            closestFocusCandidate.distance = distance;
        }
    }
}
Пример #3
0
void FocusController::findFocusableNodeInDirection(Node* outer, Node* focusedNode,
                                                   FocusDirection direction, KeyboardEvent* event,
                                                   FocusCandidate& closestFocusCandidate,
                                                   const FocusCandidate& candidateParent)
{
    ASSERT(outer);
    ASSERT(candidateParent.isNull()
        || candidateParent.node->hasTagName(frameTag)
        || candidateParent.node->hasTagName(iframeTag));

    // Walk all the child nodes and update closestFocusCandidate if we find a nearer node.
    Node* candidate = outer;
    while (candidate) {
        // Inner documents case.

        if (candidate->isFrameOwnerElement())
            deepFindFocusableNodeInDirection(candidate, focusedNode, direction, event, closestFocusCandidate);
        else if (candidate != focusedNode && candidate->isKeyboardFocusable(event)) {
            FocusCandidate currentFocusCandidate(candidate);

            // Get distance and alignment from current candidate.
            distanceDataForNode(direction, focusedNode, currentFocusCandidate);

            // Bail out if distance is maximum.
            if (currentFocusCandidate.distance == maxDistance()) {
                candidate = candidate->traverseNextNode(outer->parent());
                continue;
            }

            // If candidateParent is not null, it means that we are in a recursive call
            // from deepFineFocusableNodeInDirection (i.e. processing an element in an iframe),
            // and holds the distance and alignment data of the iframe element itself.
            if (!candidateParent.isNull()) {
                currentFocusCandidate.parentAlignment = candidateParent.alignment;
                currentFocusCandidate.parentDistance = candidateParent.distance;
            }

            updateFocusCandidateIfCloser(focusedNode, currentFocusCandidate, closestFocusCandidate);
        }

        candidate = candidate->traverseNextNode(outer->parent());
    }
}
static void updateFocusCandidateIfCloser(Node* focusedNode, const FocusCandidate& candidate, FocusCandidate& closest)
{
    // First, check the common case: neither candidate nor closest are
    // inside scrollable content, then no need to care about enclosingScrollableBox
    // heuristics or parent{Distance,Alignment}, but only distance and alignment.
    if (!candidate.inScrollableContainer() && !closest.inScrollableContainer()) {
        updateFocusCandidateInSameContainer(candidate, closest);
        return;
    }

    bool sameContainer = candidate.document() == closest.document() && candidate.enclosingScrollableBox == closest.enclosingScrollableBox;

    // Second, if candidate and closest are in the same "container" (i.e. {i}frame or any
    // scrollable block element), we can handle them as common case.
    if (sameContainer) {
        updateFocusCandidateInSameContainer(candidate, closest);
        return;
    }

    // Last, we are considering moving to a candidate located in a different enclosing
    // scrollable box than closest.
    bool isInInnerDocument = !isInRootDocument(focusedNode);

    bool sameContainerAsCandidate = isInInnerDocument ? focusedNode->document() == candidate.document() :
        focusedNode->isDescendantOf(candidate.enclosingScrollableBox);

    bool sameContainerAsClosest = isInInnerDocument ? focusedNode->document() == closest.document() :
        focusedNode->isDescendantOf(closest.enclosingScrollableBox);

    // sameContainerAsCandidate and sameContainerAsClosest are mutually exclusive.
    ASSERT(!(sameContainerAsCandidate && sameContainerAsClosest));

    if (sameContainerAsCandidate) {
        closest = candidate;
        return;
    }

    if (sameContainerAsClosest) {
        // Nothing to be done.
        return;
    }

    // NOTE: !sameContainerAsCandidate && !sameContainerAsClosest
    // If distance is shorter, and we are talking about scrollable container,
    // lets compare parent distance and alignment before anything.
    if (candidate.distance < closest.distance) {
        if (candidate.alignment >= closest.parentAlignment
         || candidate.parentAlignment == closest.parentAlignment) {
            closest = candidate;
            return;
        }

    } else if (candidate.parentDistance < closest.distance) {
        if (candidate.parentAlignment >= closest.alignment) {
            closest = candidate;
            return;
        }
    }
}
Пример #5
0
bool areElementsOnSameLine(const FocusCandidate& firstCandidate, const FocusCandidate& secondCandidate)
{
    if (firstCandidate.isNull() || secondCandidate.isNull())
        return false;

    if (!firstCandidate.visibleNode->renderer() || !secondCandidate.visibleNode->renderer())
        return false;

    if (!firstCandidate.rect.intersects(secondCandidate.rect))
        return false;

    if (firstCandidate.focusableNode->hasTagName(HTMLNames::areaTag) || secondCandidate.focusableNode->hasTagName(HTMLNames::areaTag))
        return false;

    if (!firstCandidate.visibleNode->renderer()->isRenderInline() || !secondCandidate.visibleNode->renderer()->isRenderInline())
        return false;

    if (firstCandidate.visibleNode->renderer()->containingBlock() != secondCandidate.visibleNode->renderer()->containingBlock())
        return false;

    return true;
}
Пример #6
0
// FIXME: Make this method more modular, and simpler to understand and maintain.
static void updateFocusCandidateIfCloser(Node* focusedNode, const FocusCandidate& candidate, FocusCandidate& closest)
{
    bool sameDocument = candidate.document() == closest.document();
    if (sameDocument) {
        if (closest.alignment > candidate.alignment
         || (closest.parentAlignment && candidate.alignment > closest.parentAlignment))
            return;
    } else if (closest.alignment > candidate.alignment
            && (closest.parentAlignment && candidate.alignment > closest.parentAlignment))
        return;

    if (candidate.alignment != None
     || (closest.parentAlignment >= candidate.alignment
     && closest.document() == candidate.document())) {

        // If we are now in an higher precedent case, lets reset the current closest's
        // distance so we force it to be bigger than any result we will get from
        // spatialDistance().
        if (closest.alignment < candidate.alignment
         && closest.parentAlignment < candidate.alignment)
            closest.distance = maxDistance();
    }

    // Bail out if candidate's distance is larger than that of the closest candidate.
    if (candidate.distance >= closest.distance)
        return;

    if (closest.isNull()) {
        closest = candidate;
        return;
    }

    // If the focused node and the candadate are in the same document and current
    // closest candidate is not in an {i}frame that is preferable to get focused ...
    if (focusedNode->document() == candidate.document()
        && candidate.distance < closest.parentDistance)
        closest = candidate;
    else if (focusedNode->document() != candidate.document()) {
        // If the focusedNode is in an inner document and candidate is in a
        // different document, we only consider to change focus if there is not
        // another already good focusable candidate in the same document as focusedNode.
        if (!((isInRootDocument(candidate.node) && !isInRootDocument(focusedNode))
            && focusedNode->document() == closest.document()))
            closest = candidate;
    }
}
Пример #7
0
static void updateFocusCandidateIfNeeded(FocusDirection direction, const FocusCandidate& current, FocusCandidate& candidate, FocusCandidate& closest)
{
    ASSERT(candidate.visibleNode->isElementNode());
    ASSERT(candidate.visibleNode->renderer());

    // Ignore iframes that don't have a src attribute
    if (frameOwnerElement(candidate) && (!frameOwnerElement(candidate)->contentFrame() || candidate.rect.isEmpty()))
        return;

    // Ignore off screen child nodes of containers that do not scroll (overflow:hidden)
    if (candidate.isOffscreen && !canBeScrolledIntoView(direction, candidate))
        return;

    distanceDataForNode(direction, current, candidate);
    if (candidate.distance == maxDistance())
        return;

    if (candidate.isOffscreenAfterScrolling && candidate.alignment < Full)
        return;

    if (closest.isNull()) {
        closest = candidate;
        return;
    }

    LayoutRect intersectionRect = intersection(candidate.rect, closest.rect);
    if (!intersectionRect.isEmpty() && !areElementsOnSameLine(closest, candidate)) {
        // If 2 nodes are intersecting, do hit test to find which node in on top.
        LayoutUnit x = intersectionRect.x() + intersectionRect.width() / 2;
        LayoutUnit y = intersectionRect.y() + intersectionRect.height() / 2;
        HitTestResult result = candidate.visibleNode->document()->page()->mainFrame()->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), false, true);
        if (candidate.visibleNode->contains(result.innerNode())) {
            closest = candidate;
            return;
        }
        if (closest.visibleNode->contains(result.innerNode()))
            return;
    }

    if (candidate.alignment == closest.alignment) {
        if (candidate.distance < closest.distance)
            closest = candidate;
        return;
    }

    if (candidate.alignment > closest.alignment)
        closest = candidate;
}
static void updateFocusCandidateInSameContainer(const FocusCandidate& candidate, FocusCandidate& closest)
{
    if (closest.isNull()) {
        closest = candidate;
        return;
    }

    if (candidate.alignment == closest.alignment) {
        if (candidate.distance < closest.distance)
            closest = candidate;
        return;
    }

    if (candidate.alignment > closest.alignment)
        closest = candidate;
}
void FocusController::findFocusableNodeInDirection(Node* outer, Node* focusedNode,
                                                   FocusDirection direction, KeyboardEvent* event,
                                                   FocusCandidate& closest, const FocusCandidate& candidateParent)
#endif
{
#if PLATFORM(WKC)
    CRASH_IF_STACK_OVERFLOW(WKC_STACK_MARGIN_DEFAULT);
#endif
    ASSERT(outer);
    ASSERT(candidateParent.isNull()
        || candidateParent.node->isFrameOwnerElement()
        || isScrollableContainerNode(candidateParent.node));

    // Walk all the child nodes and update closest if we find a nearer node.
    Node* node = outer;
    while (node) {

        // Inner documents case.
        if (node->isFrameOwnerElement()) {
            deepFindFocusableNodeInDirection(node, focusedNode, direction, event, closest, specificRect);
        // Scrollable block elements (e.g. <div>, etc) case.
        } else if (isScrollableContainerNode(node) && !node->renderer()->isTextArea()) {
            deepFindFocusableNodeInDirection(node, focusedNode, direction, event, closest, specificRect);
            node = node->traverseNextSibling();
            continue;

#if PLATFORM(WKC)
        } else if (node != focusedNode && node->isFocusable() && isNodeInSpecificRect(node, specificRect)) {
#else
        } else if (node != focusedNode && node->isKeyboardFocusable(event)) {
#endif
            FocusCandidate candidate(node);

            // There are two ways to identify we are in a recursive call from deepFindFocusableNodeInDirection
            // (i.e. processing an element in an iframe, frame or a scrollable block element):

            // 1) If candidateParent is not null, and it holds the distance and alignment data of the
            // parent container element itself;
            // 2) Parent of outer is <frame> or <iframe>;
            // 3) Parent is any other scrollable block element.
            if (!candidateParent.isNull()) {
                candidate.parentAlignment = candidateParent.alignment;
                candidate.parentDistance = candidateParent.distance;
                candidate.enclosingScrollableBox = candidateParent.node;

            } else if (!isInRootDocument(outer)) {
#if PLATFORM(WKC)
                if (outer->parent() && outer->parent()->isDocumentNode()) {
                    Document* document = static_cast<Document*>(outer->parent());
                    candidate.enclosingScrollableBox = static_cast<Node*>(document->ownerElement());
                }
#else
                if (Document* document = static_cast<Document*>(outer->parent()))
                    candidate.enclosingScrollableBox = static_cast<Node*>(document->ownerElement());
#endif

            } else if (isScrollableContainerNode(outer->parent()))
                candidate.enclosingScrollableBox = outer->parent();

            // Get distance and alignment from current candidate.
            distanceDataForNode(direction, focusedNode, candidate);

            // Bail out if distance is maximum.
            if (candidate.distance == maxDistance()) {
                node = node->traverseNextNode(outer->parent());
                continue;
            }

            updateFocusCandidateIfCloser(focusedNode, candidate, closest);
        }

        node = node->traverseNextNode(outer->parent());
    }
}
Пример #10
0
bool FocusController::advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection direction, KeyboardEvent* event)
{
    if (!container || !container->document())
        return false;

    LayoutRect 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()) {
        // Nothing to focus, scroll if possible.
        // NOTE: If no scrolling is performed (i.e. scrollInDirection returns false), the
        // spatial navigation algorithm will skip this container.
        return scrollInDirection(container, direction);
    }

    if (HTMLFrameOwnerElement* frameElement = frameOwnerElement(focusCandidate)) {
        // 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 (focusCandidate.isOffscreenAfterScrolling) {
            scrollInDirection(focusCandidate.visibleNode->document(), direction);
            return true;
        }
        // Navigate into a new frame.
        LayoutRect 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.visibleNode, true), direction, event);
        }
        return true;
    }

    if (canScrollInDirection(focusCandidate.visibleNode, direction)) {
        if (focusCandidate.isOffscreenAfterScrolling) {
            scrollInDirection(focusCandidate.visibleNode, direction);
            return true;
        }
        // Navigate into a new scrollable container.
        LayoutRect startingRect;
        Node* focusedNode = focusedOrMainFrame()->document()->focusedNode();
        if (focusedNode && !hasOffscreenRect(focusedNode))
            startingRect = nodeRectInAbsoluteCoordinates(focusedNode, true);
        return advanceFocusDirectionallyInContainer(focusCandidate.visibleNode, startingRect, direction, event);
    }
    if (focusCandidate.isOffscreenAfterScrolling) {
        Node* container = focusCandidate.enclosingScrollableBox;
        scrollInDirection(container, direction);
        return true;
    }

    // We found a new focus node, navigate to it.
    Element* element = toElement(focusCandidate.focusableNode);
    ASSERT(element);

    element->focus(false);
    return true;
}