Commit a6c88d89 authored by Gayane Petrosyan's avatar Gayane Petrosyan Committed by Commit Bot

[SH-Blink] Add prefix and suffix to text fragment selector

Add prefix and suffix to text fragment selector when only exact text
selector is not enough. Gradually add words from available prefix and
suffix range until unique match is found.

Bug: 1102382
Change-Id: I377deca1315b3f2ea781a2dce37d04bb5a57dd69
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2374369
Commit-Queue: Gayane Petrosyan <gayane@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarTommy Martino <tmartino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#807489}
parent 6afa37b9
...@@ -56,11 +56,11 @@ bool ShouldIgnoreContents(const Node& node) { ...@@ -56,11 +56,11 @@ bool ShouldIgnoreContents(const Node& node) {
Node* GetNonSearchableAncestor(const Node& node) { Node* GetNonSearchableAncestor(const Node& node) {
for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) { for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) {
const ComputedStyle* style = ancestor.EnsureComputedStyle(); const ComputedStyle* style = ancestor.EnsureComputedStyle();
if (ancestor.IsDocumentNode())
return nullptr;
if ((style && style->Display() == EDisplay::kNone) || if ((style && style->Display() == EDisplay::kNone) ||
ShouldIgnoreContents(ancestor)) ShouldIgnoreContents(ancestor))
return &ancestor; return &ancestor;
if (ancestor.IsDocumentNode())
return nullptr;
} }
return nullptr; return nullptr;
} }
...@@ -76,15 +76,16 @@ bool IsBlockLevel(EDisplay display) { ...@@ -76,15 +76,16 @@ bool IsBlockLevel(EDisplay display) {
display == EDisplay::kFlex || display == EDisplay::kListItem; display == EDisplay::kFlex || display == EDisplay::kListItem;
} }
// Returns the next node after |start_node| (including start node) that is a // Returns the next/previous node after |start_node| (including start node) that
// text node and is searchable and visible. // is a text node and is searchable and visible.
template <class Direction>
Node* GetVisibleTextNode(Node& start_node) { Node* GetVisibleTextNode(Node& start_node) {
Node* node = &start_node; Node* node = &start_node;
// Move to outside display none subtree if we're inside one. // Move to outside display none subtree if we're inside one.
while (Node* ancestor = GetNonSearchableAncestor(*node)) { while (Node* ancestor = GetNonSearchableAncestor(*node)) {
if (ancestor->IsDocumentNode()) if (!ancestor)
return nullptr; return nullptr;
node = FlatTreeTraversal::NextSkippingChildren(*ancestor); node = Direction::NextSkippingChildren(*ancestor);
if (!node) if (!node)
return nullptr; return nullptr;
} }
...@@ -94,7 +95,7 @@ Node* GetVisibleTextNode(Node& start_node) { ...@@ -94,7 +95,7 @@ Node* GetVisibleTextNode(Node& start_node) {
if (ShouldIgnoreContents(*node) || if (ShouldIgnoreContents(*node) ||
(style && style->Display() == EDisplay::kNone)) { (style && style->Display() == EDisplay::kNone)) {
// This element and its descendants are not visible, skip it. // This element and its descendants are not visible, skip it.
node = FlatTreeTraversal::NextSkippingChildren(*node); node = Direction::NextSkippingChildren(*node);
continue; continue;
} }
if (style && style->Visibility() == EVisibility::kVisible && if (style && style->Visibility() == EVisibility::kVisible &&
...@@ -103,7 +104,7 @@ Node* GetVisibleTextNode(Node& start_node) { ...@@ -103,7 +104,7 @@ Node* GetVisibleTextNode(Node& start_node) {
} }
// This element is hidden, but node might be visible, // This element is hidden, but node might be visible,
// or this is not a text node, so we move on. // or this is not a text node, so we move on.
node = FlatTreeTraversal::Next(*node); node = Direction::Next(*node);
} }
return nullptr; return nullptr;
} }
...@@ -195,6 +196,30 @@ Node& FindBuffer::GetFirstBlockLevelAncestorInclusive(const Node& start_node) { ...@@ -195,6 +196,30 @@ Node& FindBuffer::GetFirstBlockLevelAncestorInclusive(const Node& start_node) {
return *start_node.GetDocument().documentElement(); return *start_node.GetDocument().documentElement();
} }
Node* FindBuffer::ForwardVisibleTextNode(Node& start_node) {
struct ForwardDirection {
static Node* Next(const Node& node) {
return FlatTreeTraversal::Next(node);
}
static Node* NextSkippingChildren(const Node& node) {
return FlatTreeTraversal::NextSkippingChildren(node);
}
};
return GetVisibleTextNode<ForwardDirection>(start_node);
}
Node* FindBuffer::BackwardVisibleTextNode(Node& start_node) {
struct BackwardDirection {
static Node* Next(const Node& node) {
return FlatTreeTraversal::Previous(node);
}
static Node* NextSkippingChildren(const Node& node) {
return FlatTreeTraversal::PreviousSkippingChildren(node);
}
};
return GetVisibleTextNode<BackwardDirection>(start_node);
}
FindBuffer::Results FindBuffer::FindMatches(const WebString& search_text, FindBuffer::Results FindBuffer::FindMatches(const WebString& search_text,
const blink::FindOptions options) { const blink::FindOptions options) {
// We should return empty result if it's impossible to get a match (buffer is // We should return empty result if it's impossible to get a match (buffer is
...@@ -223,7 +248,7 @@ void FindBuffer::CollectTextUntilBlockBoundary( ...@@ -223,7 +248,7 @@ void FindBuffer::CollectTextUntilBlockBoundary(
return; return;
// Get first visible text node from |start_position|. // Get first visible text node from |start_position|.
Node* node = Node* node =
GetVisibleTextNode(*range.StartPosition().NodeAsRangeFirstNode()); ForwardVisibleTextNode(*range.StartPosition().NodeAsRangeFirstNode());
if (!node || !node->isConnected()) if (!node || !node->isConnected())
return; return;
......
...@@ -35,6 +35,10 @@ class CORE_EXPORT FindBuffer { ...@@ -35,6 +35,10 @@ class CORE_EXPORT FindBuffer {
// that is block level. // that is block level.
static Node& GetFirstBlockLevelAncestorInclusive(const Node& start_node); static Node& GetFirstBlockLevelAncestorInclusive(const Node& start_node);
// See |GetVisibleTextNode|.
static Node* ForwardVisibleTextNode(Node& start_node);
static Node* BackwardVisibleTextNode(Node& start_node);
// A match result, containing the starting position of the match and // A match result, containing the starting position of the match and
// the length of the match. // the length of the match.
struct BufferMatchResult { struct BufferMatchResult {
......
...@@ -21,6 +21,11 @@ class LocalFrame; ...@@ -21,6 +21,11 @@ class LocalFrame;
// Generated selectors would be later used to highlight the same // Generated selectors would be later used to highlight the same
// text if successfully parsed by |TextFragmentAnchor |. Generation will be // text if successfully parsed by |TextFragmentAnchor |. Generation will be
// triggered when users request "link to text" for the selected text. // triggered when users request "link to text" for the selected text.
//
// TextFragmentSelectorGenerator generates candidate selectors and tries it
// against the page content to ensure the correct and unique match. Repeats the
// process adding context/range to the selector as necessary until the correct
// match is uniquely identified or no new context/range can be added.
class CORE_EXPORT TextFragmentSelectorGenerator final class CORE_EXPORT TextFragmentSelectorGenerator final
: public GarbageCollected<TextFragmentSelectorGenerator>, : public GarbageCollected<TextFragmentSelectorGenerator>,
public TextFragmentFinder::Client, public TextFragmentFinder::Client,
...@@ -48,12 +53,57 @@ class CORE_EXPORT TextFragmentSelectorGenerator final ...@@ -48,12 +53,57 @@ class CORE_EXPORT TextFragmentSelectorGenerator final
// Notifies the results of |GenerateSelector|. // Notifies the results of |GenerateSelector|.
void NotifySelectorReady(const TextFragmentSelector& selector); void NotifySelectorReady(const TextFragmentSelector& selector);
// Wrappers for tests.
String GetAvailablePrefixAsTextForTesting(const Position& position) {
return GetAvailablePrefixAsText(position);
}
String GetAvailableSuffixAsTextForTesting(const Position& position) {
return GetAvailableSuffixAsText(position);
}
// Releases members if necessary. // Releases members if necessary.
void ClearSelection(); void ClearSelection();
void Trace(Visitor*) const; void Trace(Visitor*) const;
private: private:
// Used for determining the next step of selector generation.
enum GenerationStep { kExact, kRange, kContext };
// Used for determining the current state of |selector_|.
enum SelectorState {
// Candidate selector should be generated or extended.
kNeedsNewCandidate,
// Candidate selector generation was successful and selector is ready to be
// tested for uniqueness and accuracy by running against the page's content.
kTestCandidate,
// Candidate selector generation was unsuccessful. No further attempts are
// necessary.
kFailure,
// Selector is found. No further attempts are necessary.
kSuccess
};
void GenerateSelectorCandidate();
void ResolveSelectorState();
void RunTextFinder();
// Returns max text preceding given position that doesn't cross block
// boundaries.
String GetAvailablePrefixAsText(const Position& position);
// Returns max text following given position that doesn't cross block
// boundaries.
String GetAvailableSuffixAsText(const Position& position);
void GenerateExactSelector();
void ExtendRangeSelector();
void ExtendContext();
Member<LocalFrame> selection_frame_; Member<LocalFrame> selection_frame_;
Member<Range> selection_range_; Member<Range> selection_range_;
std::unique_ptr<TextFragmentSelector> selector_; std::unique_ptr<TextFragmentSelector> selector_;
...@@ -65,6 +115,20 @@ class CORE_EXPORT TextFragmentSelectorGenerator final ...@@ -65,6 +115,20 @@ class CORE_EXPORT TextFragmentSelectorGenerator final
selector_producer_{this, nullptr}; selector_producer_{this, nullptr};
GenerateSelectorCallback pending_generate_selector_callback_; GenerateSelectorCallback pending_generate_selector_callback_;
GenerationStep step_ = kExact;
SelectorState state_ = kNeedsNewCandidate;
// Fields used for keeping track of context.
// Strings available for gradually forming prefix and suffix.
String max_available_prefix_;
String max_available_suffix_;
// Indicates a number of words used from |max_available_prefix_| and
// |max_available_suffix_| for the current |selector_|.
int num_prefix_words_ = 0;
int num_suffix_words_ = 0;
DISALLOW_COPY_AND_ASSIGN(TextFragmentSelectorGenerator); DISALLOW_COPY_AND_ASSIGN(TextFragmentSelectorGenerator);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment