blob: a85c3477ade9267427c8fac54f4cf00f7eb0286f [file] [log] [blame]
// Copyright 2018 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_BUFFER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_BUFFER_H_
#include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
#include "third_party/blink/renderer/core/editing/finder/find_options.h"
#include "third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h"
#include "third_party/blink/renderer/core/editing/position.h"
namespace blink {
class LayoutBlockFlow;
class NGOffsetMapping;
class Node;
class WebString;
// Buffer for find-in-page, collects text until it meets a block/other
// delimiters. Uses TextSearcherICU to find match in buffer.
// See doc at https://goo.gl/rnXjBu
class CORE_EXPORT FindBuffer {
STACK_ALLOCATED();
public:
explicit FindBuffer(const EphemeralRangeInFlatTree& range);
static EphemeralRangeInFlatTree FindMatchInRange(
const EphemeralRangeInFlatTree& range,
String search_text,
const FindOptions,
base::Optional<base::TimeDelta> timeout_ms = base::nullopt);
// Returns the closest ancestor of |start_node| (including the node itself)
// that is block level.
static const Node& GetFirstBlockLevelAncestorInclusive(
const Node& start_node);
// Returns true if start and end nodes are in the same layout block flow and
// there are no nodes in between that can be considered blocks. Otherwise,
// returns false.
static bool IsInSameUninterruptedBlock(const Node& start_node,
const Node& end_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
// the length of the match.
struct BufferMatchResult {
const unsigned start;
const unsigned length;
bool operator==(const BufferMatchResult& other) const {
return start == other.start && length == other.length;
}
bool operator!=(const BufferMatchResult& other) const {
return !operator==(other);
}
};
// All match results for this buffer. We can iterate through the
// BufferMatchResults one by one using the Iterator.
class CORE_EXPORT Results {
STACK_ALLOCATED();
public:
Results();
Results(const FindBuffer& find_buffer,
TextSearcherICU* text_searcher,
const Vector<UChar>& buffer,
const String& search_text,
const blink::FindOptions options);
class CORE_EXPORT Iterator
: public std::iterator<std::forward_iterator_tag, BufferMatchResult> {
STACK_ALLOCATED();
public:
Iterator() = default;
Iterator(const FindBuffer& find_buffer,
TextSearcherICU* text_searcher,
const String& search_text);
bool operator==(const Iterator& other) {
return has_match_ == other.has_match_;
}
bool operator!=(const Iterator& other) {
return has_match_ != other.has_match_;
}
const BufferMatchResult operator*() const;
void operator++();
private:
const FindBuffer* find_buffer_;
TextSearcherICU* text_searcher_;
MatchResultICU match_;
bool has_match_ = false;
};
Iterator begin() const;
Iterator end() const;
bool IsEmpty() const;
BufferMatchResult front() const;
BufferMatchResult back() const;
unsigned CountForTesting() const;
private:
String search_text_;
const FindBuffer* find_buffer_;
TextSearcherICU* text_searcher_;
bool empty_result_ = false;
};
// Finds all the match for |search_text| in |buffer_|.
Results FindMatches(const WebString& search_text,
const blink::FindOptions options);
// Gets a flat tree range corresponding to text in the [start_index,
// end_index) of |buffer|.
EphemeralRangeInFlatTree RangeFromBufferIndex(unsigned start_index,
unsigned end_index) const;
PositionInFlatTree PositionAfterBlock() const {
if (!node_after_block_)
return PositionInFlatTree();
return PositionInFlatTree::FirstPositionInNode(*node_after_block_);
}
bool IsInvalidMatch(MatchResultICU match) const;
private:
// Collects text for one LayoutBlockFlow located within |range| to |buffer_|,
// might be stopped without finishing one full LayoutBlockFlow if we
// encountered another LayoutBLockFlow, or if the end of |range| is
// surpassed. Saves the next starting node after the block (first node in
// another LayoutBlockFlow or after |end_position|) to |node_after_block_|.
void CollectTextUntilBlockBoundary(const EphemeralRangeInFlatTree& range);
// Mapping for position in buffer -> actual node where the text came from,
// along with the offset in the NGOffsetMapping of this find_buffer.
// This is needed because when we find a match in the buffer, we want to know
// where it's located in the NGOffsetMapping for this FindBuffer.
// Example: (assume there are no whitespace)
// <div>
// aaa
// <span style="float:right;">bbb<span>ccc</span></span>
// ddd
// </div>
// We can finish FIP with three FindBuffer runs:
// Run #1, 1 BufferNodeMapping with mapping text = "aaa\uFFFCddd",
// The "\uFFFC" is the object replacement character created by the float.
// For text node "aaa", oib = 0, oim = 0.
// Content of |buffer_| = "aaa".
// Run #2, 2 BufferNodeMappings, with mapping text = "bbbccc",
// 1. For text node "bbb", oib = 0, oim = 0.
// 2. For text node "ccc", oib = 3, oim = 3.
// Content of |buffer_| = "bbbccc".
// Run #3, 1 BufferNodeMapping with mapping text = "aaa\uFFFCddd",
// For text node "ddd", oib = 0, oim = 4.
// Content of |buffer_| = "ddd".
// Since the LayoutBlockFlow for "aaa" and "ddd" is the same, they have the
// same NGOffsetMapping, the |offset_in_mapping_| for the BufferNodeMapping in
// run #3 is 4 (the index of first "d" character in the mapping text).
struct BufferNodeMapping {
const unsigned offset_in_buffer;
const unsigned offset_in_mapping;
};
const BufferNodeMapping* MappingForIndex(unsigned index) const;
PositionInFlatTree PositionAtStartOfCharacterAtIndex(unsigned index) const;
PositionInFlatTree PositionAtEndOfCharacterAtIndex(unsigned index) const;
// Adds text in |text_node| that are located within |range| to |buffer_|.
void AddTextToBuffer(const Text& text_node,
LayoutBlockFlow& block_flow,
const EphemeralRangeInFlatTree& range);
Node* node_after_block_ = nullptr;
Vector<UChar> buffer_;
Vector<BufferNodeMapping> buffer_node_mappings_;
TextSearcherICU text_searcher_;
const NGOffsetMapping* offset_mapping_ = nullptr;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_BUFFER_H_