tor-browser

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

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>