| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" |
| |
| #include "third_party/blink/renderer/core/display_lock/display_lock_context.h" |
| #include "third_party/blink/renderer/core/dom/element.h" |
| #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h" |
| #include "third_party/blink/renderer/core/dom/node.h" |
| #include "third_party/blink/renderer/core/dom/text.h" |
| #include "third_party/blink/renderer/core/editing/editing_boundary.h" |
| #include "third_party/blink/renderer/core/editing/editing_utilities.h" |
| |
| namespace blink { |
| |
| bool DisplayLockUtilities::ActivateFindInPageMatchRangeIfNeeded( |
| const EphemeralRangeInFlatTree& range) { |
| if (!RuntimeEnabledFeatures::DisplayLockingEnabled()) |
| return false; |
| DCHECK(!range.IsNull()); |
| DCHECK(!range.IsCollapsed()); |
| if (range.GetDocument().LockedDisplayLockCount() == |
| range.GetDocument().ActivationBlockingDisplayLockCount()) |
| return false; |
| // Find-in-page matches can't span multiple block-level elements (because the |
| // text will be broken by newlines between blocks), so first we find the |
| // block-level element which contains the match. |
| // This means we only need to traverse up from one node in the range, in this |
| // case we are traversing from the start position of the range. |
| Element* enclosing_block = |
| EnclosingBlock(range.StartPosition(), kCannotCrossEditingBoundary); |
| DCHECK(enclosing_block); |
| DCHECK_EQ(enclosing_block, |
| EnclosingBlock(range.EndPosition(), kCannotCrossEditingBoundary)); |
| const HeapVector<Member<Element>>& elements_to_activate = |
| ActivatableLockedInclusiveAncestors(*enclosing_block); |
| for (Element* element : elements_to_activate) { |
| // We save the elements to a vector and go through & activate them one by |
| // one like this because the DOM structure might change due to running event |
| // handlers of the beforeactivate event. |
| element->ActivateDisplayLockIfNeeded(); |
| } |
| return !elements_to_activate.IsEmpty(); |
| } |
| |
| const HeapVector<Member<Element>> |
| DisplayLockUtilities::ActivatableLockedInclusiveAncestors(Element& element) { |
| HeapVector<Member<Element>> elements_to_activate; |
| for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(element)) { |
| if (!ancestor.IsElementNode()) |
| continue; |
| if (auto* context = ToElement(ancestor).GetDisplayLockContext()) { |
| DCHECK(context->IsActivatable()); |
| if (!context->IsLocked()) |
| continue; |
| elements_to_activate.push_back(&ToElement(ancestor)); |
| } |
| } |
| return elements_to_activate; |
| } |
| |
| DisplayLockUtilities::ScopedChainForcedUpdate::ScopedChainForcedUpdate( |
| const Node* node) { |
| if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || |
| node->GetDocument().LockedDisplayLockCount() == 0) { |
| return; |
| } |
| const_cast<Node*>(node)->UpdateDistributionForFlatTreeTraversal(); |
| // TODO(vmpstr): This is somewhat inefficient, since we would pay the cost |
| // of traversing the ancestor chain even for nodes that are not in the |
| // locked subtree. We need to figure out if there is a supplementary |
| // structure that we can use to quickly identify nodes that are in the |
| // locked subtree. |
| for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*node)) { |
| if (!ancestor.IsElementNode()) |
| continue; |
| if (auto* context = ToElement(ancestor).GetDisplayLockContext()) |
| scoped_update_forced_list_.push_back(context->GetScopedForcedUpdate()); |
| } |
| } |
| |
| const Element* DisplayLockUtilities::NearestLockedInclusiveAncestor( |
| const Node& node) { |
| if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || |
| node.GetDocument().LockedDisplayLockCount() == 0 || |
| !node.CanParticipateInFlatTree()) { |
| return nullptr; |
| } |
| if (!node.IsElementNode()) |
| return NearestLockedExclusiveAncestor(node); |
| if (auto* context = ToElement(node).GetDisplayLockContext()) { |
| if (context->IsLocked()) |
| return &ToElement(node); |
| } |
| return NearestLockedExclusiveAncestor(node); |
| } |
| |
| Element* DisplayLockUtilities::NearestLockedInclusiveAncestor(Node& node) { |
| return const_cast<Element*>( |
| NearestLockedInclusiveAncestor(static_cast<const Node&>(node))); |
| } |
| |
| Element* DisplayLockUtilities::NearestLockedExclusiveAncestor( |
| const Node& node) { |
| if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || |
| node.GetDocument().LockedDisplayLockCount() == 0 || |
| !node.CanParticipateInFlatTree()) { |
| return nullptr; |
| } |
| // TODO(crbug.com/924550): Once we figure out a more efficient way to |
| // determine whether we're inside a locked subtree or not, change this. |
| for (Node& ancestor : FlatTreeTraversal::AncestorsOf(node)) { |
| if (!ancestor.IsElementNode()) |
| continue; |
| if (auto* context = ToElement(ancestor).GetDisplayLockContext()) { |
| if (context->IsLocked()) |
| return &ToElement(ancestor); |
| } |
| } |
| return nullptr; |
| } |
| |
| Element* DisplayLockUtilities::HighestLockedInclusiveAncestor( |
| const Node& node) { |
| if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || |
| node.GetDocument().LockedDisplayLockCount() == 0 || |
| !node.CanParticipateInFlatTree()) { |
| return nullptr; |
| } |
| Element* locked_ancestor = nullptr; |
| for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) { |
| if (!ancestor.IsElementNode()) |
| continue; |
| if (auto* context = ToElement(ancestor).GetDisplayLockContext()) { |
| if (context->IsLocked()) |
| locked_ancestor = &ToElement(ancestor); |
| } |
| } |
| return locked_ancestor; |
| } |
| |
| Element* DisplayLockUtilities::HighestLockedExclusiveAncestor( |
| const Node& node) { |
| if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || |
| node.GetDocument().LockedDisplayLockCount() == 0 || |
| !node.CanParticipateInFlatTree()) { |
| return nullptr; |
| } |
| if (Node* parent = FlatTreeTraversal::Parent(node)) |
| return HighestLockedInclusiveAncestor(*parent); |
| return nullptr; |
| } |
| |
| } // namespace blink |