FormControlRange-geometry-complexity-and-visibility.html (4150B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <body></body> 6 <script> 7 'use strict'; 8 const controls = ['input','textarea']; 9 10 function setupControl(control, value) { 11 document.body.innerHTML = control === 'input' 12 ? '<input type="text" id="test">' 13 : '<textarea id="test"></textarea>'; 14 const element = document.getElementById('test'); 15 element.value = value; 16 element.focus(); 17 // Stabilize layout. 18 element.style.fontFamily = 'monospace'; 19 element.style.fontSize = '16px'; 20 element.style.lineHeight = '20px'; 21 element.style.padding = '0'; 22 element.style.border = '0'; 23 element.style.margin = '8px'; 24 element.style.boxSizing = 'content-box'; 25 26 // Zero scroll offsets so client rects are relative to a known origin. 27 if ('scrollTop' in element) element.scrollTop = 0; 28 if ('scrollLeft' in element) element.scrollLeft = 0; 29 return element; 30 } 31 function assert_rect_inside(inner, outer, msg = '') { 32 const rounding = 0.5; // sub-pixel fuzz. 33 assert_greater_than_equal(inner.left + rounding, outer.left, msg + 'left inside'); 34 assert_less_than_equal(inner.right - rounding, outer.right, msg + 'right inside'); 35 } 36 37 function rect(range, element, label='') { 38 const r = range.getBoundingClientRect(); 39 assert_rect_inside(r, element.getBoundingClientRect(), label); 40 return r; 41 } 42 function setupFormControlRange(element, startOffset, endOffset) { 43 const range = new FormControlRange(); 44 range.setFormControlRange(element, startOffset, endOffset); 45 return range; 46 } 47 48 controls.forEach(controlType => { 49 // Bidirectional text: a full selection spanning LTR and RTL text 50 // should produce a non-zero bounding box within the control. 51 test(() => { 52 const mixed = 'abc \u0645\u0631\u062D\u0628\u0627 def'; // abc مرحبا def. 53 const element = setupControl(controlType, mixed); 54 const range = setupFormControlRange(element, 0, mixed.length); 55 const boundingRect = rect(range, element, 'Bidirectional text full selection inside: '); 56 assert_greater_than(boundingRect.width, 0); 57 assert_greater_than(boundingRect.height, 0); 58 }, `Bidirectional mixed text full selection geometry (${controlType})`); 59 60 test(() => { 61 // Surrogate pair handling: geometry should not split a single emoji. 62 // Check that a full pair has width, and a single code unit slice 63 // does not produce a larger width than the full pair. 64 const value = '\ud83c\udf20A\ud83c\udf20'; // 🌠A🌠 in UTF-16. 65 const element = setupControl(controlType, value); 66 67 const fullRects = Array.from(setupFormControlRange(element, 0, value.length).getClientRects()); 68 assert_greater_than_equal(fullRects.length, 1, 'full selection has rects'); 69 70 const fullWidth = fullRects[0].width; 71 const wHalf1 = Array.from(setupFormControlRange(element, 0, 1).getClientRects())[0]?.width ?? 0; 72 const wHalf2 = Array.from(setupFormControlRange(element, 1, 2).getClientRects())[0]?.width ?? 0; 73 const wPair = Array.from(setupFormControlRange(element, 0, 2).getClientRects())[0]?.width ?? 0; 74 75 assert_greater_than(wPair, 0, 'pair width greater than 0'); 76 assert_greater_than_equal(wPair + 0.05, Math.max(wHalf1, wHalf2), 'full pair width greater than or equal to any single code-unit slice'); 77 assert_greater_than(fullWidth, 0, 'full width greater than 0'); 78 }, `Surrogate pair width normalization invariants (${controlType})`); 79 80 test(() => { 81 // visibility:hidden controls still produce a caret box with zero width. 82 document.body.innerHTML = controlType === 'input' 83 ? '<input id="test" style="visibility:hidden" value="abc">' 84 : '<textarea id="test" style="visibility:hidden">abc</textarea>'; 85 const hidden = document.getElementById('test'); 86 hidden.focus(); 87 const range = setupFormControlRange(hidden, 2, 2); 88 const caretRect = rect(range, hidden, 'hidden caret inside: '); 89 assert_greater_than(caretRect.height, 0, 'caret height greater than 0'); 90 assert_approx_equals(caretRect.width, 0, 0.05, 'caret width should be 0'); 91 }, `visibility:hidden caret geometry (${controlType})`); 92 }); 93 </script>