tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

scroll-to-text-fragment.html (10344B)


      1 <!doctype html>
      2 <title>Navigating to a text fragment directive</title>
      3 <meta charset=utf-8>
      4 <link rel="help" href="https://wicg.github.io/ScrollToTextFragment/">
      5 <meta name="timeout" content="long">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="/resources/testdriver.js"></script>
      9 <script src="/resources/testdriver-vendor.js"></script>
     10 <script src="/common/utils.js"></script>
     11 <script src="stash.js"></script>
     12 <!--
     13  This test suite performs scroll to text navigations to
     14  scroll-to-text-fragment-target.html and then checks the results, which are
     15  communicated back from the target page via the WPT Stash server (see stash.py).
     16  This structure is necessary because scroll to text security restrictions
     17  specifically restrict the navigator from being able to observe the result of
     18  the navigation, e.g. the target page cannot have a window opener.
     19 -->
     20 <script>
     21 let test_cases = [
     22  // Test non-text fragment directives
     23  {
     24    fragment: '#',
     25    expect_position: 'top',
     26    description: 'Empty hash should scroll to top'
     27  },
     28  {
     29    fragment: '#:~:text=this,is,test,page',
     30    expect_position: 'top',
     31    description: 'Text directive with invalid syntax (context terms without "-") should not parse as a text directive'
     32  },
     33  {
     34    fragment: '#:~:text=foo-',
     35    expect_position: 'top',
     36    description: 'Text directive with invalid syntax (only prefix, no start text) should not parse as a text directive'
     37  },
     38  {
     39    fragment: '#:~:text=-foo',
     40    expect_position: 'top',
     41    description: 'Text directive with invalid syntax (only suffix, no start text) should not parse as a text directive'
     42  },
     43  {
     44    fragment: '#element:~:directive',
     45    expect_position: 'element',
     46    description: 'Generic fragment directive with existing element fragment should scroll to element'
     47  },
     48  {
     49    fragment: '#:~:TEXT=test',
     50    expect_position: 'top',
     51    description: 'Uppercase TEXT directive should not parse as a text directive'
     52  },
     53  // Test exact text matching, with all combinations of context terms
     54  {
     55    fragment: '#:~:text=test',
     56    expect_position: 'text',
     57    description:  'Exact text with no context should match text'
     58  },
     59  {
     60    fragment: '#:~:text=TEST',
     61    expect_position: 'text',
     62    description:  'Case-insensitive search with no context should match text'
     63  },
     64  {
     65    fragment: '#:~:text=this is a-,test',
     66    expect_position: 'text',
     67    description: 'Exact text with prefix should match text'
     68  },
     69  {
     70    fragment: '#:~:text=test,-page',
     71    expect_position: 'text',
     72    description: 'Exact text with suffix should match text'
     73  },
     74  {
     75    fragment: '#:~:text=this is a-,test,-page',
     76    expect_position: 'text',
     77    description: 'Exact text with prefix and suffix should match text'
     78  },
     79  // Test tricky edge case where prefix and query are equal
     80  {
     81    fragment: '#:~:text=foo-,foo,-bar',
     82    expect_position: 'text',
     83    description: 'Exact text with prefix and suffix and query equals prefix.'
     84  },
     85  // Test text range matching, with all combinations of context terms
     86  {
     87    fragment: '#:~:text=this,page',
     88    expect_position: 'text',
     89    description: 'Text range with no context should match text'
     90  },
     91  {
     92    fragment: '#:~:text=this-,is,test',
     93    expect_position: 'text',
     94    description: 'Text range with prefix should match text'
     95  },
     96  {
     97    fragment: '#:~:text=this,test,-page',
     98    expect_position: 'text',
     99    description: 'Text range with suffix should match text'
    100  },
    101  {
    102    fragment: '#:~:text=this-,is,test,-page',
    103    expect_position: 'text',
    104    description: 'Text range with prefix and suffix should match text'
    105  },
    106  // Test partially non-matching text ranges
    107  {
    108    fragment: '#:~:text=this,none',
    109    expect_position: 'top',
    110    description: 'Text range with non-matching endText should not match'
    111  },
    112  {
    113    fragment: '#:~:text=none,page',
    114    expect_position: 'top',
    115    description: 'Text range with non-matching startText should not match'
    116  },
    117  // Test non-matching context terms
    118  {
    119    fragment: '#:~:text=this-,is,page,-none',
    120    expect_position: 'top',
    121    description: 'Text range with prefix and nonmatching suffix should not match'
    122  },
    123  {
    124    fragment: '#:~:text=none-,this,test,-page',
    125    expect_position: 'top',
    126    description: 'Text range with nonmatching prefix and matching suffix should not match'
    127  },
    128  // Test percent encoded characters
    129  {
    130    fragment: '#:~:text=this%20is%20a%20test%20page',
    131    expect_position: 'text',
    132    description: 'Exact text with percent encoded spaces should match text'
    133  },
    134  {
    135    fragment: '#:~:text=test%20pag',
    136    expect_position: 'top',
    137    description: 'Non-whole-word exact text with spaces should not match'
    138  },
    139  {
    140    fragment: '#:~:text=%26%2C%2D',
    141    expect_position: 'text',
    142    description: 'Fragment directive with percent encoded syntactical characters "&,-" should match text'
    143  },
    144  {
    145    fragment: '#:~:text=%E3%83%8D%E3%82%B3',
    146    expect_position: 'text',
    147    description: 'Fragment directive with percent encoded non-ASCII unicode character should match text'
    148  },
    149  {
    150    fragment: '#:~:text=!$\'()*+./:;=?@_~',
    151    expect_position: 'text',
    152    description: 'Fragment directive with all TextMatchChars should match text'
    153  },
    154  // Test multiple text directives
    155  {
    156    fragment: '#:~:text=this&text=test,page',
    157    expect_position: 'text',
    158    description: 'Multiple matching exact texts should match text'
    159  },
    160  {
    161    fragment: '#:~:text=tes&text=age',
    162    expect_position: 'top',
    163    description: 'Multiple non-whole-word exact texts should not match'
    164  },
    165  {
    166    fragment: '#:~:text=none&text=test%20page',
    167    expect_position: 'text',
    168    description: 'A non-matching text directive followed by a matching text directive should match and scroll into view the second text directive'
    169  },
    170  {
    171    fragment: '#:~:text=test%20page&directive',
    172    expect_position: 'text',
    173    description: 'Text directive followed by non-text directive should match text'
    174  },
    175  {
    176    fragment: '#:~:text=test&directive&text=page',
    177    expect_position: 'text',
    178    description: 'Multiple text directives and a non-text directive should match text'
    179  },
    180  // Test text directive behavior when there's an element fragment identifier
    181  {
    182    fragment: '#element:~:text=test',
    183    expect_position: 'text',
    184    description: 'Text directive with existing element fragment should match and scroll into view text'
    185  },
    186  {
    187    fragment: '#pagestate:~:text=test',
    188    expect_position: 'text',
    189    description: 'Text directive with nonexistent element fragment should match and scroll into view text'
    190  },
    191  {
    192    fragment: '#element:~:text=nomatch',
    193    expect_position: 'element',
    194    description: 'Non-matching text directive with existing element fragment should scroll to element'
    195  },
    196  {
    197    fragment: '#pagestate:~:text=nomatch',
    198    expect_position: 'top',
    199    description: 'Non-matching text directive with nonexistent element fragment should not match and not scroll'
    200  },
    201  // Test ambiguous text matches disambiguated by context terms
    202  {
    203    fragment: '#:~:text=more-,test%20page',
    204    expect_position: 'more-text',
    205    description: 'Multiple match text directive disambiguated by prefix should match the prefixed text'
    206  },
    207  {
    208    fragment: '#:~:text=test%20page,-text',
    209    expect_position: 'more-text',
    210    description: 'Multiple match text directive disambiguated by suffix should match the suffixed text'
    211  },
    212  {
    213    fragment: '#:~:text=more-,test%20page,-text',
    214    expect_position: 'more-text',
    215    description: 'Multiple match text directive disambiguated by prefix and suffix should match the text with the given context'
    216  },
    217  // Test context terms separated by node boundaries
    218  {
    219    fragment: '#:~:text=prefix-,test%20page,-suffix',
    220    expect_position: 'cross-node-context',
    221    description: 'Text directive should match when context terms are separated by node boundaries'
    222  },
    223  // Test text directive within shadow DOM
    224  {
    225    fragment: '#:~:text=shadow%20text',
    226    expect_position: 'shadow',
    227    description: 'Text directive should match text within shadow DOM'
    228  },
    229  // Test text directive within hidden and display none elements. These cases should not scroll into
    230  // view, but still "match" in that they should be highlighted or otherwise visibly indicated
    231  // if they were to become visible.
    232  {
    233    fragment: '#:~:text=hidden%20text',
    234    expect_position: 'top',
    235    description: 'Text directive should not scroll to hidden text'
    236  },
    237  {
    238    fragment: '#:~:text=display%20none',
    239    expect_position: 'top',
    240    description: 'Text directive should not scroll to display none text'
    241  },
    242  // Test horizontal scroll into view
    243  {
    244    fragment: '#:~:text=horizontally%20scrolled%20text',
    245    expect_position: 'horizontal-scroll',
    246    description: 'Text directive should horizontally scroll into view'
    247  },
    248  {
    249    fragment: '#:~:text=Element,This',
    250    expect_position: 'element',
    251    description: 'Text directive that spans a range larger than the viewport should scroll the start into view'
    252  }
    253 ];
    254 
    255 for (const test_case of test_cases) {
    256  promise_test(t => new Promise((resolve, reject) => {
    257    let key = token();
    258 
    259    test_driver.bless('Open a URL with a text fragment directive', () => {
    260      window.open(`scroll-to-text-fragment-target.html?key=${key}${test_case.fragment}`, '_blank', 'noopener');
    261    });
    262 
    263    fetchResults(key, resolve, reject);
    264  }).then(data => {
    265    // If the position is not 'top', the :target element should be the positioned element.
    266    assert_true(data.scrollPosition == 'top' || data.target == data.scrollPosition);
    267    assert_equals(data.href.indexOf(':~:'), -1, 'Expected fragment directive to be stripped from the URL.');
    268    assert_true(data.performanceNavigationEntries.every(entry => entry.indexOf(':~:') === -1), 'Expected fragment directive to be stripped from the URL in navigation performance metrics.')
    269    assert_true(data.performanceResourcesEntries.every(entry => entry.indexOf(':~:') === -1), 'Expected fragment directive to be stripped from the URL in resource performance metrics.')
    270    assert_equals(data.scrollPosition, test_case.expect_position,
    271                  `Expected ${test_case.fragment} (${test_case.description}) to scroll to ${test_case.expect_position}.`);
    272  }), `Test navigation with fragment: ${test_case.description}.`);
    273 }
    274 </script>