1/*
2 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "ActivityState.h"
29#include "FocusDirection.h"
30#include "LayoutRect.h"
31#include "Timer.h"
32#include <wtf/Forward.h>
33#include <wtf/RefPtr.h>
34
35namespace WebCore {
36
37class ContainerNode;
38class Document;
39class Element;
40class FocusNavigationScope;
41class Frame;
42class HTMLFrameOwnerElement;
43class IntRect;
44class KeyboardEvent;
45class Node;
46class Page;
47class TreeScope;
48
49struct FocusCandidate;
50
51class FocusController {
52 WTF_MAKE_FAST_ALLOCATED;
53public:
54 explicit FocusController(Page&, OptionSet<ActivityState::Flag>);
55
56 WEBCORE_EXPORT void setFocusedFrame(Frame*);
57 Frame* focusedFrame() const { return m_focusedFrame.get(); }
58 WEBCORE_EXPORT Frame& focusedOrMainFrame() const;
59
60 WEBCORE_EXPORT bool setInitialFocus(FocusDirection, KeyboardEvent*);
61 bool advanceFocus(FocusDirection, KeyboardEvent*, bool initialFocus = false);
62
63 WEBCORE_EXPORT bool setFocusedElement(Element*, Frame&, FocusDirection = FocusDirectionNone);
64
65 void setActivityState(OptionSet<ActivityState::Flag>);
66
67 WEBCORE_EXPORT void setActive(bool);
68 bool isActive() const { return m_activityState.contains(ActivityState::WindowIsActive); }
69
70 WEBCORE_EXPORT void setFocused(bool);
71 bool isFocused() const { return m_activityState.contains(ActivityState::IsFocused); }
72
73 bool contentIsVisible() const { return m_activityState.contains(ActivityState::IsVisible); }
74
75 // These methods are used in WebCore/bindings/objc/DOM.mm.
76 WEBCORE_EXPORT Element* nextFocusableElement(Node&);
77 WEBCORE_EXPORT Element* previousFocusableElement(Node&);
78
79 void setFocusedElementNeedsRepaint();
80 Seconds timeSinceFocusWasSet() const;
81
82private:
83 void setActiveInternal(bool);
84 void setFocusedInternal(bool);
85 void setIsVisibleAndActiveInternal(bool);
86
87 bool advanceFocusDirectionally(FocusDirection, KeyboardEvent*);
88 bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus);
89
90 Element* findFocusableElementAcrossFocusScope(FocusDirection, const FocusNavigationScope& startScope, Node* start, KeyboardEvent*);
91
92 Element* findFocusableElementWithinScope(FocusDirection, const FocusNavigationScope&, Node* start, KeyboardEvent*);
93 Element* nextFocusableElementWithinScope(const FocusNavigationScope&, Node* start, KeyboardEvent*);
94 Element* previousFocusableElementWithinScope(const FocusNavigationScope&, Node* start, KeyboardEvent*);
95
96 Element* findFocusableElementDescendingIntoSubframes(FocusDirection, Element*, KeyboardEvent*);
97
98 // Searches through the given tree scope, starting from start node, for the next/previous selectable element that comes after/before start node.
99 // The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes
100 // first (from lowest to highest), and then elements without tab indexes (in document order).
101 //
102 // @param start The node from which to start searching. The node after this will be focused. May be null.
103 //
104 // @return The focus node that comes after/before start node.
105 //
106 // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
107 Element* findFocusableElementOrScopeOwner(FocusDirection, const FocusNavigationScope&, Node* start, KeyboardEvent*);
108
109 Element* findElementWithExactTabIndex(const FocusNavigationScope&, Node* start, int tabIndex, KeyboardEvent*, FocusDirection);
110
111 Element* nextFocusableElementOrScopeOwner(const FocusNavigationScope&, Node* start, KeyboardEvent*);
112 Element* previousFocusableElementOrScopeOwner(const FocusNavigationScope&, Node* start, KeyboardEvent*);
113
114 bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*);
115 void findFocusCandidateInContainer(Node& container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*, FocusCandidate& closest);
116
117 void focusRepaintTimerFired();
118
119 Page& m_page;
120 RefPtr<Frame> m_focusedFrame;
121 bool m_isChangingFocusedFrame;
122 OptionSet<ActivityState::Flag> m_activityState;
123
124 Timer m_focusRepaintTimer;
125 MonotonicTime m_focusSetTime;
126};
127
128} // namespace WebCore
129