chromium/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator_test.cc

// Copyright 2020 The Chromium Authors
// 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/fragment_directive/text_fragment_selector_generator.h"

#include <gtest/gtest.h>

#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/shared_highlighting/core/common/shared_highlighting_features.h"
#include "components/shared_highlighting/core/common/shared_highlighting_metrics.h"
#include "components/ukm/test_ukm_recorder.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
#include "third_party/blink/renderer/core/fragment_directive/text_fragment_handler.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/platform/testing/scoped_fake_ukm_recorder.h"

LinkGenerationError;

namespace blink {

namespace {
const char kSuccessUkmMetric[] =;
const char kErrorUkmMetric[] =;
}  // namespace

class TextFragmentSelectorGeneratorTest : public SimTest {};

// Basic exact selector case.
TEST_F(TextFragmentSelectorGeneratorTest, EmptySelection) {}

// Basic exact selector case.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector) {}

// A single long word will return an exact selection, even if it would normally
// exceed the max chars for exact threshold.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_Long) {}

// Exact selector test where selection contains nested <i> node.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithNestedTextNodes) {}

// Exact selector test where selection contains multiple spaces.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextWithExtraSpace) {}

// Exact selector where selection is too short, in which case context is
// required.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_TooShortNeedsContext) {}

// Exact selector with context test. Case when only one word for prefix and
// suffix is enough to disambiguate the selection.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_WithOneWordContext) {}

// Exact selector with context test. Case when multiple words for prefix and
// suffix is necessary to disambiguate the selection.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_MultipleWordContext) {}

// Exact selector with context test. Case when multiple words for prefix and
// suffix is necessary to disambiguate the selection and prefix and suffix
// contain extra space.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_MultipleWordContext_ExtraSpace) {}

// Exact selector with context test. Case when available prefix for all the
// occurrences of selected text is the same. In this case suffix should be
// extended until unique selector is found.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_SamePrefix) {}

// Exact selector with context test. Case when available suffix for all the
// occurrences of selected text is the same. In this case prefix should be
// extended until unique selector is found.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_SameSuffix) {}

// Exact selector with context test. Case when available prefix and suffix for
// all the occurrences of selected text are the same. In this case generation
// should be unsuccessful.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_SamePrefixSuffix) {}

// Exact selector with context test. Case when available prefix and suffix for
// all the occurrences of selected text are the same for the first 10 words. In
// this case generation should be unsuccessful.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_SimilarLongPreffixSuffix) {}

// Exact selector with context test. Case when no prefix is available.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_NoPrefix) {}

// Exact selector with context test. Case when no suffix is available.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_NoSuffix) {}

// Exact selector with context test. Case when available prefix is the
// preceding block.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_PrevNodePrefix) {}

// Exact selector with context test. Case when available prefix is the
// preceding block, which is a text node.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_PrevTextNodePrefix) {}

// Exact selector with context test. Case when available suffix is the next
// block.
TEST_F(TextFragmentSelectorGeneratorTest, ExactTextSelector_NextNodeSuffix) {}

// Exact selector with context test. Case when available suffix is the next
// block, which is a text node.
TEST_F(TextFragmentSelectorGeneratorTest,
       ExactTextSelector_NexttextNodeSuffix) {}

TEST_F(TextFragmentSelectorGeneratorTest, RangeSelector) {}

// It should be more than 300 characters selected from the same node so that
// ranges are used.
TEST_F(TextFragmentSelectorGeneratorTest, RangeSelector_SameNode) {}

// It should be more than 300 characters selected from the same node so that
// ranges are used.
TEST_F(TextFragmentSelectorGeneratorTest,
       RangeSelector_SameNode_MultipleSelections) {}

// When using all the selected text for the range is not enough for unique
// match, context should be added.
TEST_F(TextFragmentSelectorGeneratorTest, RangeSelector_RangeNotUnique) {}

// When selecting multiple short non block nodes, ensure range is produced
// correctly.
TEST_F(TextFragmentSelectorGeneratorTest,
       RangeSelector_RangeMultipleNonBlockNodes) {}

// When using all the selected text for the range is not enough for unique
// match, context should be added, but only prefxi and no suffix is available.
TEST_F(TextFragmentSelectorGeneratorTest,
       RangeSelector_RangeNotUnique_NoSuffix) {}

// Check the case with long word for range end.
TEST_F(TextFragmentSelectorGeneratorTest, RangeSelector_LongWord) {}

// The generator tries to include at least 3 words from the start and end of a
// range. This test ensures that the number of words used is reduced if there
// are fewer than 6 words in the selection, preventing the start and end
// overlaping.
TEST_F(TextFragmentSelectorGeneratorTest,
       RangeSelector_OverlapFailOnFirstAttempt) {}

// When range start and end overlap on second or later attempt it should stop
// adding range and start adding context.
TEST_F(TextFragmentSelectorGeneratorTest, RangeSelector_OverlapNeedsContext) {}

// Selection should be autocompleted to contain full words.
TEST_F(TextFragmentSelectorGeneratorTest, WordLimit) {}

// Selection should be autocompleted to contain full words. The autocompletion
// should work with extra spaces.
TEST_F(TextFragmentSelectorGeneratorTest, WordLimit_ExtraSpaces) {}

// When selection starts at the end of a word, selection shouldn't be
// autocompleted to contain extra words.
TEST_F(TextFragmentSelectorGeneratorTest,
       WordLimit_SelectionStartsAndEndsAtWordLimit) {}

// Check the case when selections starts with an non text node.
TEST_F(TextFragmentSelectorGeneratorTest, StartsWithImage) {}

// Check the case when selections starts with an non text node.
TEST_F(TextFragmentSelectorGeneratorTest, StartsWithBlockWithImage) {}

// Check the case when selections starts with a node nested in "inline-block"
// node. crbug.com/1151474
TEST_F(TextFragmentSelectorGeneratorTest, StartsWithInlineBlockChild) {}

// Check the case when selections ends with an non text node.
TEST_F(TextFragmentSelectorGeneratorTest, EndswithImage) {}

// Check the case when selections starts at the end of the previous block.
TEST_F(TextFragmentSelectorGeneratorTest, StartIsEndofPrevBlock) {}

// Check the case when selections starts at the end of the previous block.
TEST_F(TextFragmentSelectorGeneratorTest, EndIsStartofNextBlock) {}

// Check the case when parent of selection start is a sibling of a node where
// selection ends.
//   :root
//  /      \
// div      p
//  |       |
//  p      "]Second"
//  |
// "[First..."
// Where [] indicate selection. In this case, when the selection is adjusted, we
// want to ensure it correctly traverses the tree back to the previous text node
// and not to the <div>(sibling of second <p>).
// See crbug.com/1154308 for more context.
TEST_F(TextFragmentSelectorGeneratorTest, PrevNodeIsSiblingsChild) {}

// Check the case when parent of selection start is a sibling of a node where
// selection ends.
//    :root
//  /    |     \
// div  div     p
//  |    |       \
//  p   "test"   "]Second"
//  |
//"[First..."
//
// Where [] indicate selection. In this case, when the selection is adjusted, we
// want to ensure it correctly traverses the tree back to the previous text by
// correctly skipping the invisible div but not skipping the second <p>.
// See crbug.com/1154308 for more context.
TEST_F(TextFragmentSelectorGeneratorTest, PrevPrevNodeIsSiblingsChild) {}

// Checks that for short selection that have nested block element range selector
// is used.
TEST_F(TextFragmentSelectorGeneratorTest, RangeSelector_SameNode_Interrupted) {}

// Check min number of words is used for context if possible.
TEST_F(TextFragmentSelectorGeneratorTest, MultiwordContext) {}

// Check min number of words is used for range if possible.
TEST_F(TextFragmentSelectorGeneratorTest, MultiWordRangeSelector) {}

// Checks the case when selection end position is a non text node.
TEST_F(TextFragmentSelectorGeneratorTest, SelectionEndsWithNonText) {}

// Checks the case when selection end position is a non text node which doesn't
// have text child node.
TEST_F(TextFragmentSelectorGeneratorTest,
       SelectionEndsWithNonTextWithNoTextChild) {}

// Checks the case when selection end position is a non text node which doesn't
// have text child node.
TEST_F(TextFragmentSelectorGeneratorTest, SelectionEndsWithImageDiv) {}

// Checks the case when selected range contains a range with same start and end.
// The problematic case should have both range end and suffix.
TEST_F(TextFragmentSelectorGeneratorTest, OverlappingRange) {}

// Checks selection across table cells.
TEST_F(TextFragmentSelectorGeneratorTest, Table) {}

// Checks selection across an input element.
TEST_F(TextFragmentSelectorGeneratorTest, Input) {}

// Checks selection across a shadow tree. Input that has text value will create
// a shadow tree,
TEST_F(TextFragmentSelectorGeneratorTest, InputSubmit) {}

// Checks that haphen, ampersand and comma in selector are escaped.
// crbug.com/1245669
TEST_F(TextFragmentSelectorGeneratorTest, EscapeSelectorSpecialChars) {}

// Checks selection right after a shadow tree will use the shadow tree for
// prefix. Input with text value will create a shadow tree.
TEST_F(TextFragmentSelectorGeneratorTest, InputSubmitPrefix) {}

// Checks selection right after a shadow tree will use the shadow tree for
// prefix. Input with text value will create a shadow tree.
TEST_F(TextFragmentSelectorGeneratorTest, InputSubmitOneWordPrefix) {}

// Ensure generation works correctly when the range begins anchored to a shadow
// host. The shadow root has more children than the shadow host so this ensures
// we're using flat tree node traversals.
TEST_F(TextFragmentSelectorGeneratorTest, RangeBeginsOnShadowHost) {}

// Checks selection in multiline paragraph.
TEST_F(TextFragmentSelectorGeneratorTest, Multiline_paragraph) {}

// Checks selection in multiline paragraph.
TEST_F(TextFragmentSelectorGeneratorTest, Nbsp_before_suffix) {}

// Checks selection in multiline paragraph.
TEST_F(TextFragmentSelectorGeneratorTest, Nbsp_before_prefix) {}

// Checks that after adding max number of range words it will correctly add
// context.
TEST_F(TextFragmentSelectorGeneratorTest, ContextAfterMaxRange) {}

// Check the case when available prefix is the text content of the previous
// block.
TEST_F(TextFragmentSelectorGeneratorTest, GetPreviousTextEndPosition_PrevNode) {}

// Check the case when available prefix is a text node outside of selection
// block.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_PrevTextNode) {}

// Check the case when available prefix is a parent node text content outside of
// selection block.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_ParentNode) {}

// Check the case when previous node is used for available prefix when selection
// is not at index=0 but there is only space before it.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_SpacesBeforeSelection) {}

// Check the case when previous node is used for available prefix when selection
// is not at index=0 but there is only invisible block.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_InvisibleBeforeSelection) {}

// Check the case when available prefix complete text content of the previous
// block.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_NoPrevious) {}

// Similar test for suffix.

// Check the case when available suffix is complete text content of the next
// block.
TEST_F(TextFragmentSelectorGeneratorTest, GetNextTextStartPosition_NextNode) {}

// Check the case when there is a commented block between selection and the
// available suffix.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetNextTextStartPosition_NextNode_WithComment) {}

// Check the case when available suffix is a text node outside of selection
// block.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetNextTextStartPosition_NextTextNode) {}

// Check the case when available suffix is a parent node text content outside of
// selection block.
TEST_F(TextFragmentSelectorGeneratorTest, GetNextTextStartPosition_ParentNode) {}

// Check the case when next node is used for available suffix when selection is
// not at last index but there is only space after it.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetNextTextStartPosition_SpacesAfterSelection) {}

// Check the case when next node is used for available suffix when selection is
// not at last index but there is only invisible block after it.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetNextTextStartPosition_InvisibleAfterSelection) {}

// Check the case when available suffix is a text node outside of selection
// block.
TEST_F(TextFragmentSelectorGeneratorTest, GetNextTextStartPosition_NoNextNode) {}

TEST_F(TextFragmentSelectorGeneratorTest, BeforeAndAfterAnchor) {}

// Check the case when GetPreviousTextBlock is an EOL node from Shadow Root.
TEST_F(TextFragmentSelectorGeneratorTest,
       GetPreviousTextEndPosition_ShouldSkipNodesWithNoLayoutObject) {}

// Tests that the generator fails gracefully if the layout subtree is removed
// while we're operating on it. Reproduction for https://crbug.com/1313253.
TEST_F(TextFragmentSelectorGeneratorTest, RemoveLayoutObjectAsync) {}

}  // namespace blink