WebKit Focus Shift与Tab Key Event关联,点击TAB Key后,WebKit内核会将焦点切换到下一个焦点. 焦点变换一般通过TAB Key触发,所以首先需要了解TAB Event的牏流程。
一. Tab事件的处理
Tab Event准确说是TAB Key Event, 是在keydown阶段处理,内核处理的入口是:
EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent);
这个接口实现两个功能:
1. 找到Tab Event的Target节点 :
RefPtr<Node> node = eventTargetNodeForDocument(m_frame->document());
2. 在keydown阶段对dispatch事件到Target节点:
node->dispatchEvent(keydown, ec);
以下是WebKit在收到TAB Key Event后,进行焦点切换的堆栈:
nextNodeWithGreaterTabIndex
WebCore::FocusController::nextFocusableNode
WebCore::FocusController::findFocusableNode
WebCore::FocusController::findFocusableNodeRecursively
WebCore::FocusController::findFocusableNodeAcrossFocusScope
WebCore::FocusController::advanceFocusInDocumentOrder
WebCore::FocusController::advanceFocus
WebCore::EventHandler::defaultTabEventHandler
WebCore::EventHandler::defaultKeyboardEventHandler
WebCore::Node::defaultEventHandler
WebCore::HTMLTextFormControlElement::defaultEventHandler
WebCore::HTMLInputElement::defaultEventHandler
WebCore::EventDispatcher::dispatchEventPostProcess
WebCore::EventDispatcher::dispatchEvent
WebCore::EventDispatchMediator::dispatchEvent
WebCore::EventDispatcher::dispatchEvent
WebCore::Node::dispatchEvent
WebCore::EventTarget::dispatchEvent
WebCore::EventHandler::keyEvent
首先可以看到TAB Key Event事件的EventListener是HTMLInputElement, 在HTMLInputElement::defaultEventHandler中,会调用父类的::defautlEventHandler() ,最终传递到Node::defautlEventHandler(),该函数将找到Node所在的Frame上的EventHandler对象,调用EventHandler::defaultKeyboardEventHandler()方法处理键盘相关的事件;
EventHandler::defaultKeyboardEventHnadler(KeyboardEvent* event)方法中,根据Event的m_keyIdentifier类型来选择处理流程,如果m_keyIdentifier == "U+0009", 将调用EventHandler::defaultTabEventHandler(KeyboardEvent* event)处理Tab Key Event.
EventHandler::defaultTabEventHandler(KeyboardEvent* event)函数通过FocusController找到下一个可聚焦(Focusable)的Node, 并将焦点切换至该Node。二. 焦点的查找
从以上过程可以看出,下一个Focusable Node的查找是整个处理流程的关键步骤,在以下函数中实现:
FocusController::nextFocusableNode(FocusNavigationScope scope, Node* start, KeyboardEvent* event)
其中,
start:当前的focus node;
start是否为空, 可分两种:
- start为空:
1. 以FocusNavigationScope::rootNode()为起始点,以tree order查找tabIndex > 0的点作为winnerNode;
2. 如果winnerNode为空,从FocusNavigationScope的根节点开始,返回第一个tabIndex为0的点;
- start非空
1. 检查start是否在Tabbing Cycle中(所谓Tabbing Cycle是由指定了tabindex属性的节点组成的集合); 如果不在,则按照DOM树的结构找到下一个Focusable Node; 如果Focusable Node不存在,进入下一步;
2. 在Tabbing Cycle中,查找tabIndex == start->tabIndex()的Focusable Node,如果成功,则返回; 失败,进入下一步;
3. 继续查找tabIndex > start->tabIndex()的Focusable Node,成功则返回;失败则进入下一步;
4. 从FocusNavigationScope的根节点开始,返回第一个tabIndex为0的点;
上面的流程可以简单理解为
1. 如果参考点不在Tabbling Cycle上,以DOM树的顺序查找;
2. 优先查找tabindex相等的点;
3. 优先查找tabindex大于0的点;