tor-browser

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

test_offsets.js (9368B)


      1 var scrollbarWidth = 17,
      2  scrollbarHeight = 17;
      3 
      4 function testElements(baseid, callback) {
      5  scrollbarWidth = scrollbarHeight = gcs($("scrollbox-test"), "width");
      6 
      7  var elements = $(baseid).getElementsByTagName("*");
      8  for (var t = 0; t < elements.length; t++) {
      9    var element = elements[t];
     10 
     11    // Ignore presentational content inside menus
     12    if (
     13      element.closest("menu, menuitem") &&
     14      element.closest("[aria-hidden=true]")
     15    ) {
     16      continue;
     17    }
     18 
     19    // Ignore content inside a <button>  This can be removed if/when
     20    // button switches to use shadow DOM.
     21    let buttonParent = element.closest("button");
     22    if (buttonParent && buttonParent !== element) {
     23      continue;
     24    }
     25 
     26    testElement(element);
     27  }
     28 
     29  var nonappended = document.createElementNS(
     30    "http://www.w3.org/1999/xhtml",
     31    "div"
     32  );
     33  nonappended.id = "nonappended";
     34  nonappended.setAttribute("_offsetParent", "null");
     35  testElement(nonappended);
     36 
     37  checkScrolledElement($("scrollbox"), $("scrollchild"));
     38 
     39  var div = $("noscroll");
     40  div.scrollLeft = 10;
     41  div.scrollTop = 10;
     42  is(element.scrollLeft, 0, element.id + " scrollLeft after nonscroll");
     43  is(element.scrollTop, 0, element.id + " scrollTop after nonscroll");
     44 
     45  callback();
     46 }
     47 
     48 function usesSVGLayout(e) {
     49  return e instanceof SVGElement && !(e instanceof SVGSVGElement);
     50 }
     51 
     52 function toNearestAppunit(v) {
     53  // 60 appunits per CSS pixel; round result to the nearest appunit
     54  return Math.round(v * 60) / 60;
     55 }
     56 
     57 function isEqualAppunits(a, b, msg) {
     58  is(toNearestAppunit(a), toNearestAppunit(b), msg);
     59 }
     60 
     61 function testElement(element) {
     62  var offsetParent = element.getAttribute("_offsetParent");
     63  offsetParent = $(
     64    offsetParent == "null" ? null : offsetParent ? offsetParent : "body"
     65  );
     66 
     67  var borderLeft = gcs(element, "borderLeftWidth");
     68  var borderTop = gcs(element, "borderTopWidth");
     69  var borderRight = gcs(element, "borderRightWidth");
     70  var borderBottom = gcs(element, "borderBottomWidth");
     71  var paddingLeft = gcs(element, "paddingLeft");
     72  var paddingTop = gcs(element, "paddingTop");
     73  var paddingRight = gcs(element, "paddingRight");
     74  var paddingBottom = gcs(element, "paddingBottom");
     75  var width = gcs(element, "width");
     76  var height = gcs(element, "height");
     77 
     78  if (element instanceof HTMLElement) {
     79    checkOffsetState(
     80      element,
     81      -10000,
     82      -10000,
     83      borderLeft + paddingLeft + width + paddingRight + borderRight,
     84      borderTop + paddingTop + height + paddingBottom + borderBottom,
     85      offsetParent,
     86      element.id
     87    );
     88  }
     89 
     90  var scrollWidth, scrollHeight, clientWidth, clientHeight;
     91  var doScrollCheck = true;
     92  {
     93    if (element.id == "scrollbox") {
     94      clientWidth = paddingLeft + width + paddingRight - scrollbarWidth;
     95      clientHeight = paddingTop + height + paddingBottom - scrollbarHeight;
     96    } else {
     97      clientWidth = paddingLeft + width + paddingRight;
     98      clientHeight = paddingTop + height + paddingBottom;
     99    }
    100    if (element.id == "overflow-visible") {
    101      scrollWidth = 200;
    102      scrollHeight = 201;
    103    } else if (
    104      element.scrollWidth > clientWidth ||
    105      element.scrollHeight > clientHeight
    106    ) {
    107      // The element overflows. Don't check scrollWidth/scrollHeight since the
    108      // above calculation is not correct.
    109      doScrollCheck = false;
    110    } else {
    111      scrollWidth = clientWidth;
    112      scrollHeight = clientHeight;
    113    }
    114  }
    115 
    116  if (doScrollCheck) {
    117    if (usesSVGLayout(element)) {
    118      checkScrollState(element, 0, 0, 0, 0, element.id);
    119    } else {
    120      checkScrollState(element, 0, 0, scrollWidth, scrollHeight, element.id);
    121    }
    122  }
    123 
    124  if (usesSVGLayout(element)) {
    125    checkClientState(element, 0, 0, 0, 0, element.id);
    126  } else {
    127    checkClientState(
    128      element,
    129      borderLeft,
    130      borderTop,
    131      clientWidth,
    132      clientHeight,
    133      element.id
    134    );
    135  }
    136 
    137  var boundingrect = element.getBoundingClientRect();
    138  isEqualAppunits(
    139    boundingrect.width,
    140    borderLeft + paddingLeft + width + paddingRight + borderRight,
    141    element.id + " bounding rect width"
    142  );
    143  isEqualAppunits(
    144    boundingrect.height,
    145    borderTop + paddingTop + height + paddingBottom + borderBottom,
    146    element.id + " bounding rect height"
    147  );
    148  isEqualAppunits(
    149    boundingrect.right - boundingrect.left,
    150    boundingrect.width,
    151    element.id + " bounding rect right"
    152  );
    153  isEqualAppunits(
    154    boundingrect.bottom - boundingrect.top,
    155    boundingrect.height,
    156    element.id + " bounding rect bottom"
    157  );
    158 
    159  var rects = element.getClientRects();
    160  if (element.id == "div-displaynone" || element.id == "nonappended") {
    161    is(rects.length, 0, element.id + " getClientRects empty");
    162  } else {
    163    is(rects[0].left, boundingrect.left, element.id + " getClientRects left");
    164    is(rects[0].top, boundingrect.top, element.id + " getClientRects top");
    165    is(
    166      rects[0].right,
    167      boundingrect.right,
    168      element.id + " getClientRects right"
    169    );
    170    is(
    171      rects[0].bottom,
    172      boundingrect.bottom,
    173      element.id + " getClientRects bottom"
    174    );
    175  }
    176 }
    177 
    178 function checkScrolledElement(element, child) {
    179  var elemrect = element.getBoundingClientRect();
    180  var childrect = child.getBoundingClientRect();
    181 
    182  var topdiff = childrect.top - elemrect.top;
    183 
    184  element.scrollTop = 20;
    185  is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll");
    186  is(element.scrollTop, 20, element.id + " scrollTop after vertical scroll");
    187  // If the viewport has been transformed, then we might have scrolled to a subpixel value
    188  // that's slightly different from what we requested. After rounding, however, it should
    189  // be the same.
    190  is(
    191    Math.round(childrect.top - child.getBoundingClientRect().top),
    192    20,
    193    "child position after vertical scroll"
    194  );
    195 
    196  element.scrollTop = 0;
    197  is(
    198    element.scrollLeft,
    199    0,
    200    element.id + " scrollLeft after vertical scroll reset"
    201  );
    202  is(
    203    element.scrollTop,
    204    0,
    205    element.id + " scrollTop after vertical scroll reset"
    206  );
    207  // Scrolling back to the top should work precisely.
    208  is(
    209    child.getBoundingClientRect().top,
    210    childrect.top,
    211    "child position after vertical scroll reset"
    212  );
    213 
    214  element.scrollTop = 10;
    215  element.scrollTop = -30;
    216  is(
    217    element.scrollLeft,
    218    0,
    219    element.id + " scrollLeft after vertical scroll negative"
    220  );
    221  is(
    222    element.scrollTop,
    223    0,
    224    element.id + " scrollTop after vertical scroll negative"
    225  );
    226  is(
    227    child.getBoundingClientRect().top,
    228    childrect.top,
    229    "child position after vertical scroll negative"
    230  );
    231 
    232  element.scrollLeft = 18;
    233  is(
    234    element.scrollLeft,
    235    18,
    236    element.id + " scrollLeft after horizontal scroll"
    237  );
    238  is(element.scrollTop, 0, element.id + " scrollTop after horizontal scroll");
    239  is(
    240    Math.round(childrect.left - child.getBoundingClientRect().left),
    241    18,
    242    "child position after horizontal scroll"
    243  );
    244 
    245  element.scrollLeft = -30;
    246  is(
    247    element.scrollLeft,
    248    0,
    249    element.id + " scrollLeft after horizontal scroll reset"
    250  );
    251  is(
    252    element.scrollTop,
    253    0,
    254    element.id + " scrollTop after horizontal scroll reset"
    255  );
    256  is(
    257    child.getBoundingClientRect().left,
    258    childrect.left,
    259    "child position after horizontal scroll reset"
    260  );
    261 }
    262 
    263 function checkOffsetState(element, left, top, width, height, parent, testname) {
    264  checkCoords(element, "offset", left, top, width, height, testname);
    265  is(element.offsetParent, parent, testname + " offsetParent");
    266 }
    267 
    268 function checkScrollState(element, left, top, width, height, testname) {
    269  checkCoords(element, "scroll", left, top, width, height, testname);
    270 }
    271 
    272 function checkClientState(element, left, top, width, height, testname) {
    273  checkCoords(element, "client", left, top, width, height, testname);
    274 }
    275 
    276 function checkCoord(element, type, val, testname) {
    277  if (val != -10000) {
    278    is(element[type], Math.round(val), testname + " " + type);
    279  }
    280 }
    281 
    282 function checkCoordFuzzy(element, type, val, fuzz, testname) {
    283  if (val != -10000) {
    284    let v = element[type];
    285    ok(
    286      Math.abs(v - Math.round(val)) <= fuzz,
    287      `${testname} ${type}: ${v} vs. ${val}`
    288    );
    289  }
    290 }
    291 
    292 function checkCoords(element, type, left, top, width, height, testname) {
    293  checkCoord(element, type + "Left", left, testname);
    294  checkCoord(element, type + "Top", top, testname);
    295 
    296  if (type == "scroll") {
    297    // scrollWidth and scrollHeight can deviate by 1 pixel due to snapping.
    298    checkCoordFuzzy(element, type + "Width", width, 1, testname);
    299    checkCoordFuzzy(element, type + "Height", height, 1, testname);
    300  } else {
    301    checkCoord(element, type + "Width", width, testname);
    302    checkCoord(element, type + "Height", height, testname);
    303  }
    304 
    305  if (usesSVGLayout(element)) {
    306    return;
    307  }
    308 
    309  if (element.id == "outerpopup" && !element.parentNode.open) {
    310    // closed popup
    311    return;
    312  }
    313 
    314  if (element.id == "div-displaynone" || element.id == "nonappended") {
    315    // hidden elements
    316    ok(
    317      element[type + "Width"] == 0 && element[type + "Height"] == 0,
    318      element.id + " has zero " + type + " width and height"
    319    );
    320  }
    321 }
    322 
    323 function gcs(element, prop) {
    324  var propVal =
    325    usesSVGLayout(element) && (prop == "width" || prop == "height")
    326      ? element.getAttribute(prop)
    327      : getComputedStyle(element, "")[prop];
    328  if (propVal == "auto" || element.id == "nonappended") {
    329    return 0;
    330  }
    331  return parseFloat(propVal);
    332 }