// 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