1 /*
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nuanti Ltd.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "core/page/FocusController.h"
29
30 #include <limits>
31 #include "HTMLNames.h"
32 #include "core/accessibility/AXObjectCache.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/Element.h"
35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/NodeTraversal.h"
37 #include "core/dom/Range.h"
38 #include "core/dom/shadow/ElementShadow.h"
39 #include "core/dom/shadow/ShadowRoot.h"
40 #include "core/editing/Editor.h"
41 #include "core/editing/FrameSelection.h"
42 #include "core/editing/htmlediting.h" // For firstPositionInOrBeforeNode
43 #include "core/events/Event.h"
44 #include "core/events/ThreadLocalEventNames.h"
45 #include "core/frame/DOMWindow.h"
46 #include "core/frame/Frame.h"
47 #include "core/frame/FrameView.h"
48 #include "core/html/HTMLAreaElement.h"
49 #include "core/html/HTMLImageElement.h"
50 #include "core/html/HTMLTextAreaElement.h"
51 #include "core/html/shadow/HTMLShadowElement.h"
52 #include "core/page/Chrome.h"
53 #include "core/page/ChromeClient.h"
54 #include "core/page/EventHandler.h"
55 #include "core/page/FrameTree.h"
56 #include "core/page/Page.h"
57 #include "core/frame/Settings.h"
58 #include "core/page/SpatialNavigation.h"
59 #include "core/rendering/HitTestResult.h"
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 // FIXME: Some of Node* return values and Node* arguments should be Element*.
66
FocusNavigationScope(TreeScope * treeScope)67 FocusNavigationScope::FocusNavigationScope(TreeScope* treeScope)
68 : m_rootTreeScope(treeScope)
69 {
70 ASSERT(treeScope);
71 }
72
rootNode() const73 Node* FocusNavigationScope::rootNode() const
74 {
75 return m_rootTreeScope->rootNode();
76 }
77
owner() const78 Element* FocusNavigationScope::owner() const
79 {
80 Node* root = rootNode();
81 if (root->isShadowRoot()) {
82 ShadowRoot* shadowRoot = toShadowRoot(root);
83 return shadowRoot->isYoungest() ? shadowRoot->host() : shadowRoot->shadowInsertionPointOfYoungerShadowRoot();
84 }
85 if (Frame* frame = root->document().frame())
86 return frame->ownerElement();
87 return 0;
88 }
89
focusNavigationScopeOf(Node * node)90 FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(Node* node)
91 {
92 ASSERT(node);
93 Node* root = node;
94 for (Node* n = node; n; n = n->parentNode())
95 root = n;
96 // The result is not always a ShadowRoot nor a DocumentNode since
97 // a starting node is in an orphaned tree in composed shadow tree.
98 return FocusNavigationScope(&root->treeScope());
99 }
100
ownedByNonFocusableFocusScopeOwner(Node * node)101 FocusNavigationScope FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(Node* node)
102 {
103 ASSERT(node);
104 if (isShadowHost(node))
105 return FocusNavigationScope::ownedByShadowHost(node);
106 ASSERT(isActiveShadowInsertionPoint(*node));
107 return FocusNavigationScope::ownedByShadowInsertionPoint(toHTMLShadowElement(node));
108 }
109
ownedByShadowHost(Node * node)110 FocusNavigationScope FocusNavigationScope::ownedByShadowHost(Node* node)
111 {
112 ASSERT(isShadowHost(node));
113 return FocusNavigationScope(toElement(node)->shadow()->youngestShadowRoot());
114 }
115
ownedByIFrame(HTMLFrameOwnerElement * frame)116 FocusNavigationScope FocusNavigationScope::ownedByIFrame(HTMLFrameOwnerElement* frame)
117 {
118 ASSERT(frame && frame->contentFrame());
119 return FocusNavigationScope(frame->contentFrame()->document());
120 }
121
ownedByShadowInsertionPoint(HTMLShadowElement * shadowInsertionPoint)122 FocusNavigationScope FocusNavigationScope::ownedByShadowInsertionPoint(HTMLShadowElement* shadowInsertionPoint)
123 {
124 ASSERT(shadowInsertionPoint->isActive());
125 return FocusNavigationScope(shadowInsertionPoint->olderShadowRoot());
126 }
127
dispatchEventsOnWindowAndFocusedNode(Document * document,bool focused)128 static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool focused)
129 {
130 // If we have a focused node we should dispatch blur on it before we blur the window.
131 // If we have a focused node we should dispatch focus on it after we focus the window.
132 // https://bugs.webkit.org/show_bug.cgi?id=27105
133
134 // Do not fire events while modal dialogs are up. See https://bugs.webkit.org/show_bug.cgi?id=33962
135 if (Page* page = document->page()) {
136 if (page->defersLoading())
137 return;
138 }
139
140 if (!focused && document->focusedElement()) {
141 RefPtr<Element> focusedElement(document->focusedElement());
142 focusedElement->dispatchBlurEvent(0);
143 if (focusedElement == document->focusedElement()) {
144 focusedElement->dispatchFocusOutEvent(EventTypeNames::focusout, 0);
145 if (focusedElement == document->focusedElement())
146 focusedElement->dispatchFocusOutEvent(EventTypeNames::DOMFocusOut, 0);
147 }
148 }
149
150 if (DOMWindow* window = document->domWindow())
151 window->dispatchEvent(Event::create(focused ? EventTypeNames::focus : EventTypeNames::blur));
152 if (focused && document->focusedElement()) {
153 RefPtr<Element> focusedElement(document->focusedElement());
154 focusedElement->dispatchFocusEvent(0, FocusDirectionPage);
155 if (focusedElement == document->focusedElement()) {
156 document->focusedElement()->dispatchFocusInEvent(EventTypeNames::focusin, 0);
157 if (focusedElement == document->focusedElement())
158 document->focusedElement()->dispatchFocusInEvent(EventTypeNames::DOMFocusIn, 0);
159 }
160 }
161 }
162
hasCustomFocusLogic(Element * element)163 static inline bool hasCustomFocusLogic(Element* element)
164 {
165 return element->isHTMLElement() && toHTMLElement(element)->hasCustomFocusLogic();
166 }
167
168 #if !ASSERT_DISABLED
isNonFocusableShadowHost(Node * node)169 static inline bool isNonFocusableShadowHost(Node* node)
170 {
171 ASSERT(node);
172 if (!node->isElementNode())
173 return false;
174 Element* element = toElement(node);
175 return !element->isFocusable() && isShadowHost(element) && !hasCustomFocusLogic(element);
176 }
177 #endif
178
isNonKeyboardFocusableShadowHost(Node * node)179 static inline bool isNonKeyboardFocusableShadowHost(Node* node)
180 {
181 ASSERT(node);
182 if (!node->isElementNode())
183 return false;
184 Element* element = toElement(node);
185 return !element->isKeyboardFocusable() && isShadowHost(element) && !hasCustomFocusLogic(element);
186 }
187
isKeyboardFocusableShadowHost(Node * node)188 static inline bool isKeyboardFocusableShadowHost(Node* node)
189 {
190 ASSERT(node);
191 if (!node->isElementNode())
192 return false;
193 Element* element = toElement(node);
194 return element->isKeyboardFocusable() && isShadowHost(element) && !hasCustomFocusLogic(element);
195 }
196
isNonFocusableFocusScopeOwner(Node * node)197 static inline bool isNonFocusableFocusScopeOwner(Node* node)
198 {
199 ASSERT(node);
200 return isNonKeyboardFocusableShadowHost(node) || isActiveShadowInsertionPoint(*node);
201 }
202
adjustedTabIndex(Node * node)203 static inline int adjustedTabIndex(Node* node)
204 {
205 ASSERT(node);
206 return isNonFocusableFocusScopeOwner(node) ? 0 : node->tabIndex();
207 }
208
shouldVisit(Node * node)209 static inline bool shouldVisit(Node* node)
210 {
211 ASSERT(node);
212 return (node->isElementNode() && toElement(node)->isKeyboardFocusable()) || isNonFocusableFocusScopeOwner(node);
213 }
214
FocusController(Page * page)215 FocusController::FocusController(Page* page)
216 : m_page(page)
217 , m_isActive(false)
218 , m_isFocused(false)
219 , m_isChangingFocusedFrame(false)
220 , m_containingWindowIsVisible(false)
221 {
222 }
223
create(Page * page)224 PassOwnPtr<FocusController> FocusController::create(Page* page)
225 {
226 return adoptPtr(new FocusController(page));
227 }
228
setFocusedFrame(PassRefPtr<Frame> frame)229 void FocusController::setFocusedFrame(PassRefPtr<Frame> frame)
230 {
231 ASSERT(!frame || frame->page() == m_page);
232 if (m_focusedFrame == frame || m_isChangingFocusedFrame)
233 return;
234
235 m_isChangingFocusedFrame = true;
236
237 RefPtr<Frame> oldFrame = m_focusedFrame;
238 RefPtr<Frame> newFrame = frame;
239
240 m_focusedFrame = newFrame;
241
242 // Now that the frame is updated, fire events and update the selection focused states of both frames.
243 if (oldFrame && oldFrame->view()) {
244 oldFrame->selection().setFocused(false);
245 oldFrame->domWindow()->dispatchEvent(Event::create(EventTypeNames::blur));
246 }
247
248 if (newFrame && newFrame->view() && isFocused()) {
249 newFrame->selection().setFocused(true);
250 newFrame->domWindow()->dispatchEvent(Event::create(EventTypeNames::focus));
251 }
252
253 m_isChangingFocusedFrame = false;
254 }
255
focusedOrMainFrame() const256 Frame* FocusController::focusedOrMainFrame() const
257 {
258 if (Frame* frame = focusedFrame())
259 return frame;
260 return m_page->mainFrame();
261 }
262
setFocused(bool focused)263 void FocusController::setFocused(bool focused)
264 {
265 if (isFocused() == focused)
266 return;
267
268 m_isFocused = focused;
269
270 if (!m_isFocused)
271 focusedOrMainFrame()->eventHandler().stopAutoscroll();
272
273 if (!m_focusedFrame)
274 setFocusedFrame(m_page->mainFrame());
275
276 if (m_focusedFrame->view()) {
277 m_focusedFrame->selection().setFocused(focused);
278 dispatchEventsOnWindowAndFocusedNode(m_focusedFrame->document(), focused);
279 }
280 }
281
findFocusableNodeDecendingDownIntoFrameDocument(FocusDirection direction,Node * node)282 Node* FocusController::findFocusableNodeDecendingDownIntoFrameDocument(FocusDirection direction, Node* node)
283 {
284 // The node we found might be a HTMLFrameOwnerElement, so descend down the tree until we find either:
285 // 1) a focusable node, or
286 // 2) the deepest-nested HTMLFrameOwnerElement.
287 while (node && node->isFrameOwnerElement()) {
288 HTMLFrameOwnerElement* owner = toHTMLFrameOwnerElement(node);
289 if (!owner->contentFrame())
290 break;
291 Node* foundNode = findFocusableNode(direction, FocusNavigationScope::ownedByIFrame(owner), 0);
292 if (!foundNode)
293 break;
294 ASSERT(node != foundNode);
295 node = foundNode;
296 }
297 return node;
298 }
299
setInitialFocus(FocusDirection direction)300 bool FocusController::setInitialFocus(FocusDirection direction)
301 {
302 bool didAdvanceFocus = advanceFocus(direction, true);
303
304 // If focus is being set initially, accessibility needs to be informed that system focus has moved
305 // into the web area again, even if focus did not change within WebCore. PostNotification is called instead
306 // of handleFocusedUIElementChanged, because this will send the notification even if the element is the same.
307 if (AXObjectCache* cache = focusedOrMainFrame()->document()->existingAXObjectCache())
308 cache->postNotification(focusedOrMainFrame()->document(), AXObjectCache::AXFocusedUIElementChanged, true);
309
310 return didAdvanceFocus;
311 }
312
advanceFocus(FocusDirection direction,bool initialFocus)313 bool FocusController::advanceFocus(FocusDirection direction, bool initialFocus)
314 {
315 switch (direction) {
316 case FocusDirectionForward:
317 case FocusDirectionBackward:
318 return advanceFocusInDocumentOrder(direction, initialFocus);
319 case FocusDirectionLeft:
320 case FocusDirectionRight:
321 case FocusDirectionUp:
322 case FocusDirectionDown:
323 return advanceFocusDirectionally(direction);
324 default:
325 ASSERT_NOT_REACHED();
326 }
327
328 return false;
329 }
330
advanceFocusInDocumentOrder(FocusDirection direction,bool initialFocus)331 bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, bool initialFocus)
332 {
333 Frame* frame = focusedOrMainFrame();
334 ASSERT(frame);
335 Document* document = frame->document();
336
337 Node* currentNode = document->focusedElement();
338 // FIXME: Not quite correct when it comes to focus transitions leaving/entering the WebView itself
339 bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsingEnabled();
340
341 if (caretBrowsing && !currentNode)
342 currentNode = frame->selection().start().deprecatedNode();
343
344 document->updateLayoutIgnorePendingStylesheets();
345
346 RefPtr<Node> node = findFocusableNodeAcrossFocusScope(direction, FocusNavigationScope::focusNavigationScopeOf(currentNode ? currentNode : document), currentNode);
347
348 if (!node) {
349 // We didn't find a node to focus, so we should try to pass focus to Chrome.
350 if (!initialFocus && m_page->chrome().canTakeFocus(direction)) {
351 document->setFocusedElement(0);
352 setFocusedFrame(0);
353 m_page->chrome().takeFocus(direction);
354 return true;
355 }
356
357 // Chrome doesn't want focus, so we should wrap focus.
358 node = findFocusableNodeRecursively(direction, FocusNavigationScope::focusNavigationScopeOf(m_page->mainFrame()->document()), 0);
359 node = findFocusableNodeDecendingDownIntoFrameDocument(direction, node.get());
360
361 if (!node)
362 return false;
363 }
364
365 ASSERT(node);
366
367 if (node == document->focusedElement())
368 // Focus wrapped around to the same node.
369 return true;
370
371 if (!node->isElementNode())
372 // FIXME: May need a way to focus a document here.
373 return false;
374
375 Element* element = toElement(node);
376 if (element->isFrameOwnerElement() && (!element->isPluginElement() || !element->isKeyboardFocusable())) {
377 // We focus frames rather than frame owners.
378 // FIXME: We should not focus frames that have no scrollbars, as focusing them isn't useful to the user.
379 HTMLFrameOwnerElement* owner = toHTMLFrameOwnerElement(element);
380 if (!owner->contentFrame())
381 return false;
382
383 document->setFocusedElement(0);
384 setFocusedFrame(owner->contentFrame());
385 return true;
386 }
387
388 // FIXME: It would be nice to just be able to call setFocusedElement(node)
389 // here, but we can't do that because some elements (e.g. HTMLInputElement
390 // and HTMLTextAreaElement) do extra work in their focus() methods.
391 Document& newDocument = element->document();
392
393 if (&newDocument != document) {
394 // Focus is going away from this document, so clear the focused node.
395 document->setFocusedElement(0);
396 }
397
398 setFocusedFrame(newDocument.frame());
399
400 if (caretBrowsing) {
401 Position position = firstPositionInOrBeforeNode(element);
402 VisibleSelection newSelection(position, position, DOWNSTREAM);
403 frame->selection().setSelection(newSelection);
404 }
405
406 element->focus(false, direction);
407 return true;
408 }
409
findFocusableNodeAcrossFocusScope(FocusDirection direction,FocusNavigationScope scope,Node * currentNode)410 Node* FocusController::findFocusableNodeAcrossFocusScope(FocusDirection direction, FocusNavigationScope scope, Node* currentNode)
411 {
412 ASSERT(!currentNode || !isNonFocusableShadowHost(currentNode));
413 Node* found;
414 if (currentNode && direction == FocusDirectionForward && isKeyboardFocusableShadowHost(currentNode)) {
415 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusNavigationScope::ownedByShadowHost(currentNode), 0);
416 found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableNodeRecursively(direction, scope, currentNode);
417 } else {
418 found = findFocusableNodeRecursively(direction, scope, currentNode);
419 }
420
421 // If there's no focusable node to advance to, move up the focus scopes until we find one.
422 while (!found) {
423 Node* owner = scope.owner();
424 if (!owner)
425 break;
426 scope = FocusNavigationScope::focusNavigationScopeOf(owner);
427 if (direction == FocusDirectionBackward && isKeyboardFocusableShadowHost(owner)) {
428 found = owner;
429 break;
430 }
431 found = findFocusableNodeRecursively(direction, scope, owner);
432 }
433 found = findFocusableNodeDecendingDownIntoFrameDocument(direction, found);
434 return found;
435 }
436
findFocusableNodeRecursively(FocusDirection direction,FocusNavigationScope scope,Node * start)437 Node* FocusController::findFocusableNodeRecursively(FocusDirection direction, FocusNavigationScope scope, Node* start)
438 {
439 // Starting node is exclusive.
440 Node* found = findFocusableNode(direction, scope, start);
441 if (!found)
442 return 0;
443 if (direction == FocusDirectionForward) {
444 if (!isNonFocusableFocusScopeOwner(found))
445 return found;
446 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(found), 0);
447 return foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableNodeRecursively(direction, scope, found);
448 }
449 ASSERT(direction == FocusDirectionBackward);
450 if (isKeyboardFocusableShadowHost(found)) {
451 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusNavigationScope::ownedByShadowHost(found), 0);
452 return foundInInnerFocusScope ? foundInInnerFocusScope : found;
453 }
454 if (isNonFocusableFocusScopeOwner(found)) {
455 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(found), 0);
456 return foundInInnerFocusScope ? foundInInnerFocusScope :findFocusableNodeRecursively(direction, scope, found);
457 }
458 return found;
459 }
460
findFocusableNode(FocusDirection direction,FocusNavigationScope scope,Node * node)461 Node* FocusController::findFocusableNode(FocusDirection direction, FocusNavigationScope scope, Node* node)
462 {
463 return (direction == FocusDirectionForward)
464 ? nextFocusableNode(scope, node)
465 : previousFocusableNode(scope, node);
466 }
467
findNodeWithExactTabIndex(Node * start,int tabIndex,FocusDirection direction)468 Node* FocusController::findNodeWithExactTabIndex(Node* start, int tabIndex, FocusDirection direction)
469 {
470 // Search is inclusive of start
471 for (Node* node = start; node; node = direction == FocusDirectionForward ? NodeTraversal::next(*node) : NodeTraversal::previous(*node)) {
472 if (shouldVisit(node) && adjustedTabIndex(node) == tabIndex)
473 return node;
474 }
475 return 0;
476 }
477
nextNodeWithGreaterTabIndex(Node * start,int tabIndex)478 static Node* nextNodeWithGreaterTabIndex(Node* start, int tabIndex)
479 {
480 // Search is inclusive of start
481 int winningTabIndex = std::numeric_limits<short>::max() + 1;
482 Node* winner = 0;
483 for (Node* node = start; node; node = NodeTraversal::next(*node)) {
484 if (shouldVisit(node) && node->tabIndex() > tabIndex && node->tabIndex() < winningTabIndex) {
485 winner = node;
486 winningTabIndex = node->tabIndex();
487 }
488 }
489
490 return winner;
491 }
492
previousNodeWithLowerTabIndex(Node * start,int tabIndex)493 static Node* previousNodeWithLowerTabIndex(Node* start, int tabIndex)
494 {
495 // Search is inclusive of start
496 int winningTabIndex = 0;
497 Node* winner = 0;
498 for (Node* node = start; node; node = NodeTraversal::previous(*node)) {
499 int currentTabIndex = adjustedTabIndex(node);
500 if ((shouldVisit(node) || isNonKeyboardFocusableShadowHost(node)) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
501 winner = node;
502 winningTabIndex = currentTabIndex;
503 }
504 }
505 return winner;
506 }
507
nextFocusableNode(FocusNavigationScope scope,Node * start)508 Node* FocusController::nextFocusableNode(FocusNavigationScope scope, Node* start)
509 {
510 if (start) {
511 int tabIndex = adjustedTabIndex(start);
512 // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order
513 if (tabIndex < 0) {
514 for (Node* node = NodeTraversal::next(*start); node; node = NodeTraversal::next(*node)) {
515 if (shouldVisit(node) && adjustedTabIndex(node) >= 0)
516 return node;
517 }
518 }
519
520 // First try to find a node with the same tabindex as start that comes after start in the scope.
521 if (Node* winner = findNodeWithExactTabIndex(NodeTraversal::next(*start), tabIndex, FocusDirectionForward))
522 return winner;
523
524 if (!tabIndex)
525 // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order.
526 return 0;
527 }
528
529 // Look for the first node in the scope that:
530 // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and
531 // 2) comes first in the scope, if there's a tie.
532 if (Node* winner = nextNodeWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(start) : 0))
533 return winner;
534
535 // There are no nodes with a tabindex greater than start's tabindex,
536 // so find the first node with a tabindex of 0.
537 return findNodeWithExactTabIndex(scope.rootNode(), 0, FocusDirectionForward);
538 }
539
previousFocusableNode(FocusNavigationScope scope,Node * start)540 Node* FocusController::previousFocusableNode(FocusNavigationScope scope, Node* start)
541 {
542 Node* last = 0;
543 for (Node* node = scope.rootNode(); node; node = node->lastChild())
544 last = node;
545 ASSERT(last);
546
547 // First try to find the last node in the scope that comes before start and has the same tabindex as start.
548 // If start is null, find the last node in the scope with a tabindex of 0.
549 Node* startingNode;
550 int startingTabIndex;
551 if (start) {
552 startingNode = NodeTraversal::previous(*start);
553 startingTabIndex = adjustedTabIndex(start);
554 } else {
555 startingNode = last;
556 startingTabIndex = 0;
557 }
558
559 // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order
560 if (startingTabIndex < 0) {
561 for (Node* node = startingNode; node; node = NodeTraversal::previous(*node)) {
562 if (shouldVisit(node) && adjustedTabIndex(node) >= 0)
563 return node;
564 }
565 }
566
567 if (Node* winner = findNodeWithExactTabIndex(startingNode, startingTabIndex, FocusDirectionBackward))
568 return winner;
569
570 // There are no nodes before start with the same tabindex as start, so look for a node that:
571 // 1) has the highest non-zero tabindex (that is less than start's tabindex), and
572 // 2) comes last in the scope, if there's a tie.
573 startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits<short>::max();
574 return previousNodeWithLowerTabIndex(last, startingTabIndex);
575 }
576
relinquishesEditingFocus(Node * node)577 static bool relinquishesEditingFocus(Node *node)
578 {
579 ASSERT(node);
580 ASSERT(node->rendererIsEditable());
581 return node->document().frame() && node->rootEditableElement();
582 }
583
clearSelectionIfNeeded(Frame * oldFocusedFrame,Frame * newFocusedFrame,Node * newFocusedNode)584 static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFrame, Node* newFocusedNode)
585 {
586 if (!oldFocusedFrame || !newFocusedFrame)
587 return;
588
589 if (oldFocusedFrame->document() != newFocusedFrame->document())
590 return;
591
592 FrameSelection& selection = oldFocusedFrame->selection();
593 if (selection.isNone())
594 return;
595
596 bool caretBrowsing = oldFocusedFrame->settings()->caretBrowsingEnabled();
597 if (caretBrowsing)
598 return;
599
600 Node* selectionStartNode = selection.selection().start().deprecatedNode();
601 if (selectionStartNode == newFocusedNode || selectionStartNode->isDescendantOf(newFocusedNode) || selectionStartNode->deprecatedShadowAncestorNode() == newFocusedNode)
602 return;
603
604 if (Node* mousePressNode = newFocusedFrame->eventHandler().mousePressNode()) {
605 if (mousePressNode->renderer() && !mousePressNode->canStartSelection()) {
606 // Don't clear the selection for contentEditable elements, but do clear it for input and textarea. See bug 38696.
607 Node* root = selection.rootEditableElement();
608 if (!root)
609 return;
610
611 if (Node* shadowAncestorNode = root->deprecatedShadowAncestorNode()) {
612 if (!shadowAncestorNode->hasTagName(inputTag) && !isHTMLTextAreaElement(shadowAncestorNode))
613 return;
614 }
615 }
616 }
617
618 selection.clear();
619 }
620
setFocusedElement(Element * element,PassRefPtr<Frame> newFocusedFrame,FocusDirection direction)621 bool FocusController::setFocusedElement(Element* element, PassRefPtr<Frame> newFocusedFrame, FocusDirection direction)
622 {
623 RefPtr<Frame> oldFocusedFrame = focusedFrame();
624 RefPtr<Document> oldDocument = oldFocusedFrame ? oldFocusedFrame->document() : 0;
625
626 Element* oldFocusedElement = oldDocument ? oldDocument->focusedElement() : 0;
627 if (element && oldFocusedElement == element)
628 return true;
629
630 // FIXME: Might want to disable this check for caretBrowsing
631 if (oldFocusedElement && oldFocusedElement->isRootEditableElement() && !relinquishesEditingFocus(oldFocusedElement))
632 return false;
633
634 m_page->chrome().client().willSetInputMethodState();
635
636 RefPtr<Document> newDocument;
637 if (element)
638 newDocument = &element->document();
639 else if (newFocusedFrame)
640 newDocument = newFocusedFrame->document();
641
642 if (newDocument && oldDocument == newDocument && newDocument->focusedElement() == element)
643 return true;
644
645 clearSelectionIfNeeded(oldFocusedFrame.get(), newFocusedFrame.get(), element);
646
647 if (oldDocument && oldDocument != newDocument)
648 oldDocument->setFocusedElement(0);
649
650 if (newFocusedFrame && !newFocusedFrame->page()) {
651 setFocusedFrame(0);
652 return false;
653 }
654 setFocusedFrame(newFocusedFrame);
655
656 // Setting the focused node can result in losing our last reft to node when JS event handlers fire.
657 RefPtr<Element> protect = element;
658 if (newDocument) {
659 bool successfullyFocused = newDocument->setFocusedElement(element, direction);
660 if (!successfullyFocused)
661 return false;
662 }
663
664 return true;
665 }
666
setActive(bool active)667 void FocusController::setActive(bool active)
668 {
669 if (m_isActive == active)
670 return;
671
672 m_isActive = active;
673
674 if (FrameView* view = m_page->mainFrame()->view()) {
675 view->updateLayoutAndStyleIfNeededRecursive();
676 view->updateControlTints();
677 }
678
679 focusedOrMainFrame()->selection().pageActivationChanged();
680 }
681
contentAreaDidShowOrHide(ScrollableArea * scrollableArea,bool didShow)682 static void contentAreaDidShowOrHide(ScrollableArea* scrollableArea, bool didShow)
683 {
684 if (didShow)
685 scrollableArea->contentAreaDidShow();
686 else
687 scrollableArea->contentAreaDidHide();
688 }
689
setContainingWindowIsVisible(bool containingWindowIsVisible)690 void FocusController::setContainingWindowIsVisible(bool containingWindowIsVisible)
691 {
692 if (m_containingWindowIsVisible == containingWindowIsVisible)
693 return;
694
695 m_containingWindowIsVisible = containingWindowIsVisible;
696
697 FrameView* view = m_page->mainFrame()->view();
698 if (!view)
699 return;
700
701 contentAreaDidShowOrHide(view, containingWindowIsVisible);
702
703 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
704 FrameView* frameView = frame->view();
705 if (!frameView)
706 continue;
707
708 const HashSet<ScrollableArea*>* scrollableAreas = frameView->scrollableAreas();
709 if (!scrollableAreas)
710 continue;
711
712 for (HashSet<ScrollableArea*>::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
713 ScrollableArea* scrollableArea = *it;
714 ASSERT(scrollableArea->scrollbarsCanBeActive());
715
716 contentAreaDidShowOrHide(scrollableArea, containingWindowIsVisible);
717 }
718 }
719 }
720
updateFocusCandidateIfNeeded(FocusDirection direction,const FocusCandidate & current,FocusCandidate & candidate,FocusCandidate & closest)721 static void updateFocusCandidateIfNeeded(FocusDirection direction, const FocusCandidate& current, FocusCandidate& candidate, FocusCandidate& closest)
722 {
723 ASSERT(candidate.visibleNode->isElementNode());
724 ASSERT(candidate.visibleNode->renderer());
725
726 // Ignore iframes that don't have a src attribute
727 if (frameOwnerElement(candidate) && (!frameOwnerElement(candidate)->contentFrame() || candidate.rect.isEmpty()))
728 return;
729
730 // Ignore off screen child nodes of containers that do not scroll (overflow:hidden)
731 if (candidate.isOffscreen && !canBeScrolledIntoView(direction, candidate))
732 return;
733
734 distanceDataForNode(direction, current, candidate);
735 if (candidate.distance == maxDistance())
736 return;
737
738 if (candidate.isOffscreenAfterScrolling && candidate.alignment < Full)
739 return;
740
741 if (closest.isNull()) {
742 closest = candidate;
743 return;
744 }
745
746 LayoutRect intersectionRect = intersection(candidate.rect, closest.rect);
747 if (!intersectionRect.isEmpty() && !areElementsOnSameLine(closest, candidate)) {
748 // If 2 nodes are intersecting, do hit test to find which node in on top.
749 LayoutUnit x = intersectionRect.x() + intersectionRect.width() / 2;
750 LayoutUnit y = intersectionRect.y() + intersectionRect.height() / 2;
751 HitTestResult result = candidate.visibleNode->document().page()->mainFrame()->eventHandler().hitTestResultAtPoint(IntPoint(x, y), HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
752 if (candidate.visibleNode->contains(result.innerNode())) {
753 closest = candidate;
754 return;
755 }
756 if (closest.visibleNode->contains(result.innerNode()))
757 return;
758 }
759
760 if (candidate.alignment == closest.alignment) {
761 if (candidate.distance < closest.distance)
762 closest = candidate;
763 return;
764 }
765
766 if (candidate.alignment > closest.alignment)
767 closest = candidate;
768 }
769
findFocusCandidateInContainer(Node & container,const LayoutRect & startingRect,FocusDirection direction,FocusCandidate & closest)770 void FocusController::findFocusCandidateInContainer(Node& container, const LayoutRect& startingRect, FocusDirection direction, FocusCandidate& closest)
771 {
772 Element* focusedElement = (focusedFrame() && focusedFrame()->document()) ? focusedFrame()->document()->focusedElement() : 0;
773
774 Element* element = ElementTraversal::firstWithin(container);
775 FocusCandidate current;
776 current.rect = startingRect;
777 current.focusableNode = focusedElement;
778 current.visibleNode = focusedElement;
779
780 for (; element; element = (element->isFrameOwnerElement() || canScrollInDirection(element, direction))
781 ? ElementTraversal::nextSkippingChildren(*element, &container)
782 : ElementTraversal::next(*element, &container)) {
783 if (element == focusedElement)
784 continue;
785
786 if (!element->isKeyboardFocusable() && !element->isFrameOwnerElement() && !canScrollInDirection(element, direction))
787 continue;
788
789 FocusCandidate candidate = FocusCandidate(element, direction);
790 if (candidate.isNull())
791 continue;
792
793 candidate.enclosingScrollableBox = &container;
794 updateFocusCandidateIfNeeded(direction, current, candidate, closest);
795 }
796 }
797
advanceFocusDirectionallyInContainer(Node * container,const LayoutRect & startingRect,FocusDirection direction)798 bool FocusController::advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection direction)
799 {
800 if (!container)
801 return false;
802
803 LayoutRect newStartingRect = startingRect;
804
805 if (startingRect.isEmpty())
806 newStartingRect = virtualRectForDirection(direction, nodeRectInAbsoluteCoordinates(container));
807
808 // Find the closest node within current container in the direction of the navigation.
809 FocusCandidate focusCandidate;
810 findFocusCandidateInContainer(*container, newStartingRect, direction, focusCandidate);
811
812 if (focusCandidate.isNull()) {
813 // Nothing to focus, scroll if possible.
814 // NOTE: If no scrolling is performed (i.e. scrollInDirection returns false), the
815 // spatial navigation algorithm will skip this container.
816 return scrollInDirection(container, direction);
817 }
818
819 if (HTMLFrameOwnerElement* frameElement = frameOwnerElement(focusCandidate)) {
820 // If we have an iframe without the src attribute, it will not have a contentFrame().
821 // We ASSERT here to make sure that
822 // updateFocusCandidateIfNeeded() will never consider such an iframe as a candidate.
823 ASSERT(frameElement->contentFrame());
824
825 if (focusCandidate.isOffscreenAfterScrolling) {
826 scrollInDirection(&focusCandidate.visibleNode->document(), direction);
827 return true;
828 }
829 // Navigate into a new frame.
830 LayoutRect rect;
831 Element* focusedElement = focusedOrMainFrame()->document()->focusedElement();
832 if (focusedElement && !hasOffscreenRect(focusedElement))
833 rect = nodeRectInAbsoluteCoordinates(focusedElement, true /* ignore border */);
834 frameElement->contentFrame()->document()->updateLayoutIgnorePendingStylesheets();
835 if (!advanceFocusDirectionallyInContainer(frameElement->contentFrame()->document(), rect, direction)) {
836 // The new frame had nothing interesting, need to find another candidate.
837 return advanceFocusDirectionallyInContainer(container, nodeRectInAbsoluteCoordinates(focusCandidate.visibleNode, true), direction);
838 }
839 return true;
840 }
841
842 if (canScrollInDirection(focusCandidate.visibleNode, direction)) {
843 if (focusCandidate.isOffscreenAfterScrolling) {
844 scrollInDirection(focusCandidate.visibleNode, direction);
845 return true;
846 }
847 // Navigate into a new scrollable container.
848 LayoutRect startingRect;
849 Element* focusedElement = focusedOrMainFrame()->document()->focusedElement();
850 if (focusedElement && !hasOffscreenRect(focusedElement))
851 startingRect = nodeRectInAbsoluteCoordinates(focusedElement, true);
852 return advanceFocusDirectionallyInContainer(focusCandidate.visibleNode, startingRect, direction);
853 }
854 if (focusCandidate.isOffscreenAfterScrolling) {
855 Node* container = focusCandidate.enclosingScrollableBox;
856 scrollInDirection(container, direction);
857 return true;
858 }
859
860 // We found a new focus node, navigate to it.
861 Element* element = toElement(focusCandidate.focusableNode);
862 ASSERT(element);
863
864 element->focus(false, direction);
865 return true;
866 }
867
advanceFocusDirectionally(FocusDirection direction)868 bool FocusController::advanceFocusDirectionally(FocusDirection direction)
869 {
870 Frame* curFrame = focusedOrMainFrame();
871 ASSERT(curFrame);
872
873 Document* focusedDocument = curFrame->document();
874 if (!focusedDocument)
875 return false;
876
877 Element* focusedElement = focusedDocument->focusedElement();
878 Node* container = focusedDocument;
879
880 if (container->isDocumentNode())
881 toDocument(container)->updateLayoutIgnorePendingStylesheets();
882
883 // Figure out the starting rect.
884 LayoutRect startingRect;
885 if (focusedElement) {
886 if (!hasOffscreenRect(focusedElement)) {
887 container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, focusedElement);
888 startingRect = nodeRectInAbsoluteCoordinates(focusedElement, true /* ignore border */);
889 } else if (isHTMLAreaElement(focusedElement)) {
890 HTMLAreaElement* area = toHTMLAreaElement(focusedElement);
891 container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, area->imageElement());
892 startingRect = virtualRectForAreaElementAndDirection(area, direction);
893 }
894 }
895
896 bool consumed = false;
897 do {
898 consumed = advanceFocusDirectionallyInContainer(container, startingRect, direction);
899 startingRect = nodeRectInAbsoluteCoordinates(container, true /* ignore border */);
900 container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, container);
901 if (container && container->isDocumentNode())
902 toDocument(container)->updateLayoutIgnorePendingStylesheets();
903 } while (!consumed && container);
904
905 return consumed;
906 }
907
908 } // namespace WebCore
909