| <!DOCTYPE html> |
| <title>Percent-encoding in a text directive</title> |
| <meta charset=utf-8> |
| <link rel="help" href="https://wicg.github.io/ScrollToTextFragment/"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/resources/testdriver.js"></script> |
| <script src="/resources/testdriver-vendor.js"></script> |
| <script src="/common/utils.js"></script> |
| <script src="stash.js"></script> |
| <script src="resources/util.js"></script> |
| <style> |
| .target { |
| margin-top: 2000px; |
| margin-bottom: 2000px; |
| } |
| </style> |
| <script> |
| const params = new URLSearchParams(location.search); |
| const is_popup = params.has('key'); |
| |
| // This test opens a popup to this document which invokes a text directive. The |
| // popup checks the result of navigating the text directive and communicates it |
| // back to the original page via Stash (since text fragments require noopener). |
| if (is_popup) { |
| const key = params.get('key'); |
| |
| function sendResult() { |
| let result = 'unknown'; |
| if (window.scrollY == 0) |
| result = 'noscroll'; |
| else { |
| for (let target of document.querySelectorAll('.target')) { |
| if (isInViewport(target)) { |
| result = target.id; |
| break; |
| } |
| } |
| } |
| |
| stashResultsThenClose(key, result); |
| } |
| |
| // Ensure two animation frames on load to test the fallback to element anchor, |
| // which gets queued for the next frame if the text fragment is not found. |
| onload = () => { requestAnimationFrame( () => requestAnimationFrame(sendResult) ); }; |
| } else { |
| let test_cases = [ |
| { |
| fragment: '#:~:text=%25', |
| expect: 'singlepercent', |
| description: 'Percent-encoded "%" char.' |
| }, |
| { |
| fragment: '#:~:text=%', |
| expect: 'noscroll', |
| description: 'Percent char without hex digits is invalid.' |
| }, |
| { |
| fragment: '#:~:text=%%', |
| expect: 'noscroll', |
| description: 'Percent char followed by percent char is invalid.' |
| }, |
| { |
| fragment: '#:~:text=%F', |
| expect: 'noscroll', |
| description: 'Single digit percent-encoding is invalid.' |
| }, |
| { |
| fragment: '#:~:text=%25F', |
| expect: 'percentf', |
| description: 'Percent-encoding limited to two digits.' |
| }, |
| { |
| fragment: '#:~:text=%25%25F', |
| expect: 'doublepercentf', |
| description: 'Percent-encoded "%%F"' |
| }, |
| { |
| fragment: '#:~:text=%E2%9C%85', |
| expect: 'checkmark', |
| description: 'Percent-encoding multibyte codepoint (CHECKMARK).' |
| }, |
| ]; |
| |
| for (const test_case of test_cases) { |
| promise_test(t => new Promise((resolve, reject) => { |
| let key = token(); |
| |
| test_driver.bless('Open a URL with a text fragment directive', () => { |
| window.open(`${location.href}?key=${key}${test_case.fragment}`, '_blank', 'noopener'); |
| }); |
| |
| fetchResults(key, resolve, reject); |
| }).then(result => { |
| assert_equals(result, test_case.expect); |
| }), `Test navigation with fragment: ${test_case.description}.`); |
| } |
| } |
| </script> |
| |
| <p class="target" id="singlepercent"> |
| % |
| </p> |
| <p class="target" id="doublepercent"> |
| %% |
| </p> |
| <p class="target" id="percentf"> |
| %F |
| </p> |
| <p class="target" id="doublepercentf"> |
| %%f |
| </p> |
| <p class="target" id="checkmark"> |
| <!-- U+2705 WHITE HEAVY CHECK MARK - UTF-8 percent encoding: %E2%9C%85 --> |
| ✅ |
| </p> |
| <p class="target" id="helloworld"> |
| Hello world |
| </p> |
| |