| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |