Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(227)

Side by Side Diff: third_party/WebKit/Source/core/page/FocusController.cpp

Issue 2562753003: Fix focus navigation getting stuck (Closed)
Patch Set: Refactor ScopedFocusNavigation class. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/LayoutTests/shadow-dom/crashes/focus-navigation-infinite-loop.html ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nuanti Ltd. 3 * Copyright (C) 2008 Nuanti Ltd.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
69 69
70 inline bool isShadowInsertionPointFocusScopeOwner(Element& element) { 70 inline bool isShadowInsertionPointFocusScopeOwner(Element& element) {
71 return isActiveShadowInsertionPoint(element) && 71 return isActiveShadowInsertionPoint(element) &&
72 toHTMLShadowElement(element).olderShadowRoot(); 72 toHTMLShadowElement(element).olderShadowRoot();
73 } 73 }
74 74
75 class ScopedFocusNavigation { 75 class ScopedFocusNavigation {
76 STACK_ALLOCATED(); 76 STACK_ALLOCATED();
77 77
78 public: 78 public:
79 Element* currentElement() const; 79 // Searches through the given tree scope, starting from start element, for
80 void setCurrentElement(Element*); 80 // the next/previous selectable element that comes after/before start element.
81 void moveToNext(); 81 // The order followed is as specified in the HTML spec[1], which is elements
82 void moveToPrevious(); 82 // with tab indexes first (from lowest to highest), and then elements without
83 void moveToFirst(); 83 // tab indexes (in document order). The search algorithm also conforms the
84 void moveToLast(); 84 // Shadow DOM spec[2], which inserts sequence in a shadow tree into its host.
85 //
86 // @param start The element from which to start searching. The element after
87 // this will be focused. May be null.
88 // @return The focus element that comes after/before start element.
89 //
90 // [1]
91 // https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-na vigation
92 // [2] https://w3c.github.io/webcomponents/spec/shadow/#focus-navigation
93 Element* findFocusableElement(WebFocusType type) {
94 return (type == WebFocusTypeForward) ? nextFocusableElement()
95 : previousFocusableElement();
96 }
97
98 Element* currentElement() const { return m_current; }
85 Element* owner() const; 99 Element* owner() const;
100
86 static ScopedFocusNavigation createFor(const Element&); 101 static ScopedFocusNavigation createFor(const Element&);
87 static ScopedFocusNavigation createForDocument(Document&); 102 static ScopedFocusNavigation createForDocument(Document&);
88 static ScopedFocusNavigation ownedByNonFocusableFocusScopeOwner(Element&); 103 static ScopedFocusNavigation ownedByNonFocusableFocusScopeOwner(Element&);
89 static ScopedFocusNavigation ownedByShadowHost(const Element&); 104 static ScopedFocusNavigation ownedByShadowHost(const Element&);
90 static ScopedFocusNavigation ownedByShadowInsertionPoint(HTMLShadowElement&); 105 static ScopedFocusNavigation ownedByShadowInsertionPoint(HTMLShadowElement&);
91 static ScopedFocusNavigation ownedByHTMLSlotElement(const HTMLSlotElement&); 106 static ScopedFocusNavigation ownedByHTMLSlotElement(const HTMLSlotElement&);
92 static ScopedFocusNavigation ownedByIFrame(const HTMLFrameOwnerElement&); 107 static ScopedFocusNavigation ownedByIFrame(const HTMLFrameOwnerElement&);
93 static HTMLSlotElement* findFallbackScopeOwnerSlot(const Element&); 108 static HTMLSlotElement* findFallbackScopeOwnerSlot(const Element&);
94 static bool isSlotFallbackScoped(const Element&); 109 static bool isSlotFallbackScoped(const Element&);
95 static bool isSlotFallbackScopedForThisSlot(const HTMLSlotElement&, 110 static bool isSlotFallbackScopedForThisSlot(const HTMLSlotElement&,
96 const Element&); 111 const Element&);
97 112
98 private: 113 private:
99 ScopedFocusNavigation(TreeScope&, const Element*); 114 ScopedFocusNavigation(TreeScope&, const Element*);
100 ScopedFocusNavigation(HTMLSlotElement&, const Element*); 115 ScopedFocusNavigation(HTMLSlotElement&, const Element*);
116
117 Element* findElementWithExactTabIndex(int tabIndex, WebFocusType);
118 Element* nextElementWithGreaterTabIndex(int tabIndex);
119 Element* previousElementWithLowerTabIndex(int tabIndex);
120 Element* nextFocusableElement();
121 Element* previousFocusableElement();
122
123 void setCurrentElement(Element* element) { m_current = element; }
124 void moveToNext();
125 void moveToPrevious();
126 void moveToFirst();
127 void moveToLast();
128
101 Member<ContainerNode> m_rootNode; 129 Member<ContainerNode> m_rootNode;
102 Member<HTMLSlotElement> m_rootSlot; 130 Member<HTMLSlotElement> m_rootSlot;
103 Member<Element> m_current; 131 Member<Element> m_current;
104 bool m_slotFallbackTraversal; 132 bool m_slotFallbackTraversal;
105 }; 133 };
106 134
107 ScopedFocusNavigation::ScopedFocusNavigation(TreeScope& treeScope, 135 ScopedFocusNavigation::ScopedFocusNavigation(TreeScope& treeScope,
108 const Element* current) 136 const Element* current)
109 : m_rootNode(treeScope.rootNode()), 137 : m_rootNode(treeScope.rootNode()),
110 m_rootSlot(nullptr), 138 m_rootSlot(nullptr),
111 m_current(const_cast<Element*>(current)) {} 139 m_current(const_cast<Element*>(current)) {}
112 140
113 ScopedFocusNavigation::ScopedFocusNavigation(HTMLSlotElement& slot, 141 ScopedFocusNavigation::ScopedFocusNavigation(HTMLSlotElement& slot,
114 const Element* current) 142 const Element* current)
115 : m_rootNode(nullptr), 143 : m_rootNode(nullptr),
116 m_rootSlot(&slot), 144 m_rootSlot(&slot),
117 m_current(const_cast<Element*>(current)), 145 m_current(const_cast<Element*>(current)),
118 m_slotFallbackTraversal(slot.assignedNodes().isEmpty()) {} 146 m_slotFallbackTraversal(slot.assignedNodes().isEmpty()) {}
119 147
120 Element* ScopedFocusNavigation::currentElement() const {
121 return m_current;
122 }
123
124 void ScopedFocusNavigation::setCurrentElement(Element* element) {
125 m_current = element;
126 }
127
128 void ScopedFocusNavigation::moveToNext() { 148 void ScopedFocusNavigation::moveToNext() {
129 DCHECK(m_current); 149 DCHECK(m_current);
130 if (m_rootSlot) { 150 if (m_rootSlot) {
131 if (m_slotFallbackTraversal) { 151 if (m_slotFallbackTraversal) {
132 m_current = ElementTraversal::next(*m_current, m_rootSlot); 152 m_current = ElementTraversal::next(*m_current, m_rootSlot);
133 while (m_current && 153 while (m_current &&
134 !ScopedFocusNavigation::isSlotFallbackScopedForThisSlot( 154 !ScopedFocusNavigation::isSlotFallbackScopedForThisSlot(
135 *m_rootSlot, *m_current)) 155 *m_rootSlot, *m_current))
136 m_current = ElementTraversal::next(*m_current, m_rootSlot); 156 m_current = ElementTraversal::next(*m_current, m_rootSlot);
137 } else { 157 } else {
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after
403 isShadowInsertionPointFocusScopeOwner(element)) 423 isShadowInsertionPointFocusScopeOwner(element))
404 ? 0 424 ? 0
405 : element.tabIndex(); 425 : element.tabIndex();
406 } 426 }
407 427
408 inline bool shouldVisit(Element& element) { 428 inline bool shouldVisit(Element& element) {
409 return element.isKeyboardFocusable() || 429 return element.isKeyboardFocusable() ||
410 isNonFocusableFocusScopeOwner(element); 430 isNonFocusableFocusScopeOwner(element);
411 } 431 }
412 432
413 Element* findElementWithExactTabIndex(ScopedFocusNavigation& scope, 433 Element* ScopedFocusNavigation::findElementWithExactTabIndex(
414 int tabIndex, 434 int tabIndex,
415 WebFocusType type) { 435 WebFocusType type) {
416 // Search is inclusive of start 436 // Search is inclusive of start
417 for (; scope.currentElement(); type == WebFocusTypeForward 437 for (; currentElement();
418 ? scope.moveToNext() 438 type == WebFocusTypeForward ? moveToNext() : moveToPrevious()) {
419 : scope.moveToPrevious()) { 439 Element* current = currentElement();
420 Element* current = scope.currentElement();
421 if (shouldVisit(*current) && adjustedTabIndex(*current) == tabIndex) 440 if (shouldVisit(*current) && adjustedTabIndex(*current) == tabIndex)
422 return current; 441 return current;
423 } 442 }
424 return nullptr; 443 return nullptr;
425 } 444 }
426 445
427 Element* nextElementWithGreaterTabIndex(ScopedFocusNavigation& scope, 446 Element* ScopedFocusNavigation::nextElementWithGreaterTabIndex(int tabIndex) {
428 int tabIndex) {
429 // Search is inclusive of start 447 // Search is inclusive of start
430 int winningTabIndex = std::numeric_limits<int>::max(); 448 int winningTabIndex = std::numeric_limits<int>::max();
431 Element* winner = nullptr; 449 Element* winner = nullptr;
432 for (; scope.currentElement(); scope.moveToNext()) { 450 for (; currentElement(); moveToNext()) {
433 Element* current = scope.currentElement(); 451 Element* current = currentElement();
434 int currentTabIndex = adjustedTabIndex(*current); 452 int currentTabIndex = adjustedTabIndex(*current);
435 if (shouldVisit(*current) && currentTabIndex > tabIndex) { 453 if (shouldVisit(*current) && currentTabIndex > tabIndex) {
436 if (!winner || currentTabIndex < winningTabIndex) { 454 if (!winner || currentTabIndex < winningTabIndex) {
437 winner = current; 455 winner = current;
438 winningTabIndex = currentTabIndex; 456 winningTabIndex = currentTabIndex;
439 } 457 }
440 } 458 }
441 } 459 }
460 setCurrentElement(winner);
442 return winner; 461 return winner;
443 } 462 }
444 463
445 Element* previousElementWithLowerTabIndex(ScopedFocusNavigation& scope, 464 Element* ScopedFocusNavigation::previousElementWithLowerTabIndex(int tabIndex) {
446 int tabIndex) {
447 // Search is inclusive of start 465 // Search is inclusive of start
448 int winningTabIndex = 0; 466 int winningTabIndex = 0;
449 Element* winner = nullptr; 467 Element* winner = nullptr;
450 for (; scope.currentElement(); scope.moveToPrevious()) { 468 for (; currentElement(); moveToPrevious()) {
451 Element* current = scope.currentElement(); 469 Element* current = currentElement();
452 int currentTabIndex = adjustedTabIndex(*current); 470 int currentTabIndex = adjustedTabIndex(*current);
453 if (shouldVisit(*current) && currentTabIndex < tabIndex && 471 if (shouldVisit(*current) && currentTabIndex < tabIndex &&
454 currentTabIndex > winningTabIndex) { 472 currentTabIndex > winningTabIndex) {
455 winner = current; 473 winner = current;
456 winningTabIndex = currentTabIndex; 474 winningTabIndex = currentTabIndex;
457 } 475 }
458 } 476 }
477 setCurrentElement(winner);
459 return winner; 478 return winner;
460 } 479 }
461 480
462 Element* nextFocusableElement(ScopedFocusNavigation& scope) { 481 Element* ScopedFocusNavigation::nextFocusableElement() {
463 Element* current = scope.currentElement(); 482 Element* current = currentElement();
464 if (current) { 483 if (current) {
465 int tabIndex = adjustedTabIndex(*current); 484 int tabIndex = adjustedTabIndex(*current);
466 // If an element is excluded from the normal tabbing cycle, the next 485 // If an element is excluded from the normal tabbing cycle, the next
467 // focusable element is determined by tree order. 486 // focusable element is determined by tree order.
468 if (tabIndex < 0) { 487 if (tabIndex < 0) {
469 for (scope.moveToNext(); scope.currentElement(); scope.moveToNext()) { 488 for (moveToNext(); currentElement(); moveToNext()) {
470 current = scope.currentElement(); 489 current = currentElement();
471 if (shouldVisit(*current) && adjustedTabIndex(*current) >= 0) 490 if (shouldVisit(*current) && adjustedTabIndex(*current) >= 0)
472 return current; 491 return current;
473 } 492 }
474 } else { 493 } else {
475 // First try to find an element with the same tabindex as start that comes 494 // First try to find an element with the same tabindex as start that comes
476 // after start in the scope. 495 // after start in the scope.
477 scope.moveToNext(); 496 moveToNext();
478 if (Element* winner = findElementWithExactTabIndex(scope, tabIndex, 497 if (Element* winner =
479 WebFocusTypeForward)) 498 findElementWithExactTabIndex(tabIndex, WebFocusTypeForward))
480 return winner; 499 return winner;
481 } 500 }
482 if (!tabIndex) { 501 if (!tabIndex) {
483 // We've reached the last element in the document with a tabindex of 0. 502 // We've reached the last element in the document with a tabindex of 0.
484 // This is the end of the tabbing order. 503 // This is the end of the tabbing order.
485 return nullptr; 504 return nullptr;
486 } 505 }
487 } 506 }
488 507
489 // Look for the first element in the scope that: 508 // Look for the first element in the scope that:
490 // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if 509 // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if
491 // start is null), and 510 // start is null), and
492 // 2) comes first in the scope, if there's a tie. 511 // 2) comes first in the scope, if there's a tie.
493 scope.moveToFirst(); 512 moveToFirst();
494 if (Element* winner = nextElementWithGreaterTabIndex( 513 if (Element* winner = nextElementWithGreaterTabIndex(
495 scope, current ? adjustedTabIndex(*current) : 0)) { 514 current ? adjustedTabIndex(*current) : 0)) {
496 return winner; 515 return winner;
497 } 516 }
498 517
499 // There are no elements with a tabindex greater than start's tabindex, 518 // There are no elements with a tabindex greater than start's tabindex,
500 // so find the first element with a tabindex of 0. 519 // so find the first element with a tabindex of 0.
501 scope.moveToFirst(); 520 moveToFirst();
502 return findElementWithExactTabIndex(scope, 0, WebFocusTypeForward); 521 return findElementWithExactTabIndex(0, WebFocusTypeForward);
503 } 522 }
504 523
505 Element* previousFocusableElement(ScopedFocusNavigation& scope) { 524 Element* ScopedFocusNavigation::previousFocusableElement() {
506 // First try to find the last element in the scope that comes before start and 525 // First try to find the last element in the scope that comes before start and
507 // has the same tabindex as start. If start is null, find the last element in 526 // has the same tabindex as start. If start is null, find the last element in
508 // the scope with a tabindex of 0. 527 // the scope with a tabindex of 0.
509 int tabIndex; 528 int tabIndex;
510 Element* current = scope.currentElement(); 529 Element* current = currentElement();
511 if (current) { 530 if (current) {
512 scope.moveToPrevious(); 531 moveToPrevious();
513 tabIndex = adjustedTabIndex(*current); 532 tabIndex = adjustedTabIndex(*current);
514 } else { 533 } else {
515 scope.moveToLast(); 534 moveToLast();
516 tabIndex = 0; 535 tabIndex = 0;
517 } 536 }
518 537
519 // However, if an element is excluded from the normal tabbing cycle, the 538 // However, if an element is excluded from the normal tabbing cycle, the
520 // previous focusable element is determined by tree order 539 // previous focusable element is determined by tree order
521 if (tabIndex < 0) { 540 if (tabIndex < 0) {
522 for (; scope.currentElement(); scope.moveToPrevious()) { 541 for (; currentElement(); moveToPrevious()) {
523 current = scope.currentElement(); 542 current = currentElement();
524 if (shouldVisit(*current) && adjustedTabIndex(*current) >= 0) 543 if (shouldVisit(*current) && adjustedTabIndex(*current) >= 0)
525 return current; 544 return current;
526 } 545 }
527 } else { 546 } else {
528 if (Element* winner = 547 if (Element* winner =
529 findElementWithExactTabIndex(scope, tabIndex, WebFocusTypeBackward)) 548 findElementWithExactTabIndex(tabIndex, WebFocusTypeBackward))
530 return winner; 549 return winner;
531 } 550 }
532 551
533 // There are no elements before start with the same tabindex as start, so look 552 // There are no elements before start with the same tabindex as start, so look
534 // for an element that: 553 // for an element that:
535 // 1) has the highest non-zero tabindex (that is less than start's tabindex), 554 // 1) has the highest non-zero tabindex (that is less than start's tabindex),
536 // and 555 // and
537 // 2) comes last in the scope, if there's a tie. 556 // 2) comes last in the scope, if there's a tie.
538 tabIndex = (current && tabIndex) ? tabIndex : std::numeric_limits<int>::max(); 557 tabIndex = (current && tabIndex) ? tabIndex : std::numeric_limits<int>::max();
539 scope.moveToLast(); 558 moveToLast();
540 return previousElementWithLowerTabIndex(scope, tabIndex); 559 return previousElementWithLowerTabIndex(tabIndex);
541 }
542
543 // Searches through the given tree scope, starting from start element, for the
544 // next/previous selectable element that comes after/before start element.
545 // The order followed is as specified in the HTML spec[1], which is elements
546 // with tab indexes first (from lowest to highest), and then elements without
547 // tab indexes (in document order). The search algorithm also conforms the
548 // Shadow DOM spec[2], which inserts sequence in a shadow tree into its host.
549 //
550 // @param start The element from which to start searching. The element after
551 // this will be focused. May be null.
552 // @return The focus element that comes after/before start element.
553 //
554 // [1]
555 // https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navi gation
556 // [2] https://w3c.github.io/webcomponents/spec/shadow/#focus-navigation
557 inline Element* findFocusableElementInternal(WebFocusType type,
558 ScopedFocusNavigation& scope) {
559 Element* found = (type == WebFocusTypeForward)
560 ? nextFocusableElement(scope)
561 : previousFocusableElement(scope);
562 return found;
563 } 560 }
564 561
565 Element* findFocusableElementRecursivelyForward(ScopedFocusNavigation& scope) { 562 Element* findFocusableElementRecursivelyForward(ScopedFocusNavigation& scope) {
566 // Starting element is exclusive. 563 // Starting element is exclusive.
567 Element* found = findFocusableElementInternal(WebFocusTypeForward, scope); 564 while (Element* found = scope.findFocusableElement(WebFocusTypeForward)) {
568 while (found) {
569 if (isShadowHostDelegatesFocus(*found)) { 565 if (isShadowHostDelegatesFocus(*found)) {
570 // If tabindex is positive, find focusable element inside its shadow tree. 566 // If tabindex is positive, find focusable element inside its shadow tree.
571 if (found->tabIndex() >= 0 && 567 if (found->tabIndex() >= 0 &&
572 isShadowHostWithoutCustomFocusLogic(*found)) { 568 isShadowHostWithoutCustomFocusLogic(*found)) {
573 ScopedFocusNavigation innerScope = 569 ScopedFocusNavigation innerScope =
574 ScopedFocusNavigation::ownedByShadowHost(*found); 570 ScopedFocusNavigation::ownedByShadowHost(*found);
575 if (Element* foundInInnerFocusScope = 571 if (Element* foundInInnerFocusScope =
576 findFocusableElementRecursivelyForward(innerScope)) 572 findFocusableElementRecursivelyForward(innerScope))
577 return foundInInnerFocusScope; 573 return foundInInnerFocusScope;
578 } 574 }
579 // Skip to the next element in the same scope. 575 // Skip to the next element in the same scope.
580 found = findFocusableElementInternal(WebFocusTypeForward, scope);
581 continue; 576 continue;
582 } 577 }
583 if (!isNonFocusableFocusScopeOwner(*found)) 578 if (!isNonFocusableFocusScopeOwner(*found))
584 return found; 579 return found;
585 580
586 // Now |found| is on a non focusable scope owner (either shadow host or 581 // Now |found| is on a non focusable scope owner (either shadow host or
587 // <shadow> or slot) Find inside the inward scope and return it if found. 582 // <shadow> or slot) Find inside the inward scope and return it if found.
588 // Otherwise continue searching in the same scope. 583 // Otherwise continue searching in the same scope.
589 ScopedFocusNavigation innerScope = 584 ScopedFocusNavigation innerScope =
590 ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(*found); 585 ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(*found);
591 if (Element* foundInInnerFocusScope = 586 if (Element* foundInInnerFocusScope =
592 findFocusableElementRecursivelyForward(innerScope)) 587 findFocusableElementRecursivelyForward(innerScope))
593 return foundInInnerFocusScope; 588 return foundInInnerFocusScope;
594
595 scope.setCurrentElement(found);
596 found = findFocusableElementInternal(WebFocusTypeForward, scope);
597 } 589 }
598 return nullptr; 590 return nullptr;
599 } 591 }
600 592
601 Element* findFocusableElementRecursivelyBackward(ScopedFocusNavigation& scope) { 593 Element* findFocusableElementRecursivelyBackward(ScopedFocusNavigation& scope) {
602 // Starting element is exclusive. 594 // Starting element is exclusive.
603 Element* found = findFocusableElementInternal(WebFocusTypeBackward, scope); 595 while (Element* found = scope.findFocusableElement(WebFocusTypeBackward)) {
604
605 while (found) {
606 // Now |found| is on a focusable shadow host. 596 // Now |found| is on a focusable shadow host.
607 // Find inside shadow backwards. If any focusable element is found, return 597 // Find inside shadow backwards. If any focusable element is found, return
608 // it, otherwise return the host itself. 598 // it, otherwise return the host itself.
609 if (isKeyboardFocusableShadowHost(*found)) { 599 if (isKeyboardFocusableShadowHost(*found)) {
610 ScopedFocusNavigation innerScope = 600 ScopedFocusNavigation innerScope =
611 ScopedFocusNavigation::ownedByShadowHost(*found); 601 ScopedFocusNavigation::ownedByShadowHost(*found);
612 Element* foundInInnerFocusScope = 602 Element* foundInInnerFocusScope =
613 findFocusableElementRecursivelyBackward(innerScope); 603 findFocusableElementRecursivelyBackward(innerScope);
614 if (foundInInnerFocusScope) 604 if (foundInInnerFocusScope)
615 return foundInInnerFocusScope; 605 return foundInInnerFocusScope;
616 if (isShadowHostDelegatesFocus(*found)) { 606 if (isShadowHostDelegatesFocus(*found))
617 found = findFocusableElementInternal(WebFocusTypeBackward, scope);
618 continue; 607 continue;
619 }
620 return found; 608 return found;
621 } 609 }
622 610
623 // If delegatesFocus is true and tabindex is negative, skip the whole shadow 611 // If delegatesFocus is true and tabindex is negative, skip the whole shadow
624 // tree under the shadow host. 612 // tree under the shadow host.
625 if (isShadowHostDelegatesFocus(*found) && found->tabIndex() < 0) { 613 if (isShadowHostDelegatesFocus(*found) && found->tabIndex() < 0)
626 found = findFocusableElementInternal(WebFocusTypeBackward, scope);
627 continue; 614 continue;
628 }
629 615
630 // Now |found| is on a non focusable scope owner (either shadow host or 616 // Now |found| is on a non focusable scope owner (a shadow host, a <shadow>
631 // <shadow> or slot). Find focusable element in descendant scope. If not 617 // or a slot). Find focusable element in descendant scope. If not found,
632 // found, find next focusable element within the current scope. 618 // find the next focusable element within the current scope.
633 if (isNonFocusableFocusScopeOwner(*found)) { 619 if (isNonFocusableFocusScopeOwner(*found)) {
634 ScopedFocusNavigation innerScope = 620 ScopedFocusNavigation innerScope =
635 ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(*found); 621 ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(*found);
636 Element* foundInInnerFocusScope = 622 if (Element* foundInInnerFocusScope =
637 findFocusableElementRecursivelyBackward(innerScope); 623 findFocusableElementRecursivelyBackward(innerScope))
638
639 if (foundInInnerFocusScope)
640 return foundInInnerFocusScope; 624 return foundInInnerFocusScope;
641 found = findFocusableElementInternal(WebFocusTypeBackward, scope);
642 continue; 625 continue;
643 } 626 }
644 if (!isShadowHostDelegatesFocus(*found)) 627 if (!isShadowHostDelegatesFocus(*found))
645 return found; 628 return found;
646
647 scope.setCurrentElement(found);
648 found = findFocusableElementInternal(WebFocusTypeBackward, scope);
649 } 629 }
650 return nullptr; 630 return nullptr;
651 } 631 }
652 632
653 Element* findFocusableElementRecursively(WebFocusType type, 633 Element* findFocusableElementRecursively(WebFocusType type,
654 ScopedFocusNavigation& scope) { 634 ScopedFocusNavigation& scope) {
655 return (type == WebFocusTypeForward) 635 return (type == WebFocusTypeForward)
656 ? findFocusableElementRecursivelyForward(scope) 636 ? findFocusableElementRecursivelyForward(scope)
657 : findFocusableElementRecursivelyBackward(scope); 637 : findFocusableElementRecursivelyBackward(scope);
658 } 638 }
(...skipping 754 matching lines...) Expand 10 before | Expand all | Expand 10 after
1413 1393
1414 return consumed; 1394 return consumed;
1415 } 1395 }
1416 1396
1417 DEFINE_TRACE(FocusController) { 1397 DEFINE_TRACE(FocusController) {
1418 visitor->trace(m_page); 1398 visitor->trace(m_page);
1419 visitor->trace(m_focusedFrame); 1399 visitor->trace(m_focusedFrame);
1420 } 1400 }
1421 1401
1422 } // namespace blink 1402 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/shadow-dom/crashes/focus-navigation-infinite-loop.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698