332 lines
9.1 KiB
JavaScript
332 lines
9.1 KiB
JavaScript
var scrollbarWidth = 17,
|
|
scrollbarHeight = 17;
|
|
|
|
function testElements(baseid, callback) {
|
|
scrollbarWidth = scrollbarHeight = gcs($("scrollbox-test"), "width");
|
|
|
|
var elements = $(baseid).getElementsByTagName("*");
|
|
for (var t = 0; t < elements.length; t++) {
|
|
var element = elements[t];
|
|
|
|
// Ignore presentational content inside menus
|
|
if (
|
|
element.closest("menu, menuitem") &&
|
|
element.closest("[aria-hidden=true]")
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
// Ignore content inside a <button> This can be removed if/when
|
|
// button switches to use shadow DOM.
|
|
let buttonParent = element.closest("button");
|
|
if (buttonParent && buttonParent !== element) {
|
|
continue;
|
|
}
|
|
|
|
testElement(element);
|
|
}
|
|
|
|
var nonappended = document.createElementNS(
|
|
"http://www.w3.org/1999/xhtml",
|
|
"div"
|
|
);
|
|
nonappended.id = "nonappended";
|
|
nonappended.setAttribute("_offsetParent", "null");
|
|
testElement(nonappended);
|
|
|
|
checkScrolledElement($("scrollbox"), $("scrollchild"));
|
|
|
|
var div = $("noscroll");
|
|
div.scrollLeft = 10;
|
|
div.scrollTop = 10;
|
|
is(element.scrollLeft, 0, element.id + " scrollLeft after nonscroll");
|
|
is(element.scrollTop, 0, element.id + " scrollTop after nonscroll");
|
|
|
|
callback();
|
|
}
|
|
|
|
function usesSVGLayout(e) {
|
|
return e instanceof SVGElement && !(e instanceof SVGSVGElement);
|
|
}
|
|
|
|
function toNearestAppunit(v) {
|
|
// 60 appunits per CSS pixel; round result to the nearest appunit
|
|
return Math.round(v * 60) / 60;
|
|
}
|
|
|
|
function isEqualAppunits(a, b, msg) {
|
|
is(toNearestAppunit(a), toNearestAppunit(b), msg);
|
|
}
|
|
|
|
function testElement(element) {
|
|
var offsetParent = element.getAttribute("_offsetParent");
|
|
offsetParent = $(
|
|
offsetParent == "null" ? null : offsetParent ? offsetParent : "body"
|
|
);
|
|
|
|
var borderLeft = gcs(element, "borderLeftWidth");
|
|
var borderTop = gcs(element, "borderTopWidth");
|
|
var borderRight = gcs(element, "borderRightWidth");
|
|
var borderBottom = gcs(element, "borderBottomWidth");
|
|
var paddingLeft = gcs(element, "paddingLeft");
|
|
var paddingTop = gcs(element, "paddingTop");
|
|
var paddingRight = gcs(element, "paddingRight");
|
|
var paddingBottom = gcs(element, "paddingBottom");
|
|
var width = gcs(element, "width");
|
|
var height = gcs(element, "height");
|
|
|
|
if (element instanceof HTMLElement) {
|
|
checkOffsetState(
|
|
element,
|
|
-10000,
|
|
-10000,
|
|
borderLeft + paddingLeft + width + paddingRight + borderRight,
|
|
borderTop + paddingTop + height + paddingBottom + borderBottom,
|
|
offsetParent,
|
|
element.id
|
|
);
|
|
}
|
|
|
|
var scrollWidth, scrollHeight, clientWidth, clientHeight;
|
|
var doScrollCheck = true;
|
|
{
|
|
if (element.id == "scrollbox") {
|
|
clientWidth = paddingLeft + width + paddingRight - scrollbarWidth;
|
|
clientHeight = paddingTop + height + paddingBottom - scrollbarHeight;
|
|
} else {
|
|
clientWidth = paddingLeft + width + paddingRight;
|
|
clientHeight = paddingTop + height + paddingBottom;
|
|
}
|
|
if (element.id == "overflow-visible") {
|
|
scrollWidth = 200;
|
|
scrollHeight = 201;
|
|
} else if (
|
|
element.scrollWidth > clientWidth ||
|
|
element.scrollHeight > clientHeight
|
|
) {
|
|
// The element overflows. Don't check scrollWidth/scrollHeight since the
|
|
// above calculation is not correct.
|
|
doScrollCheck = false;
|
|
} else {
|
|
scrollWidth = clientWidth;
|
|
scrollHeight = clientHeight;
|
|
}
|
|
}
|
|
|
|
if (doScrollCheck) {
|
|
if (usesSVGLayout(element)) {
|
|
checkScrollState(element, 0, 0, 0, 0, element.id);
|
|
} else {
|
|
checkScrollState(element, 0, 0, scrollWidth, scrollHeight, element.id);
|
|
}
|
|
}
|
|
|
|
if (usesSVGLayout(element)) {
|
|
checkClientState(element, 0, 0, 0, 0, element.id);
|
|
} else {
|
|
checkClientState(
|
|
element,
|
|
borderLeft,
|
|
borderTop,
|
|
clientWidth,
|
|
clientHeight,
|
|
element.id
|
|
);
|
|
}
|
|
|
|
var boundingrect = element.getBoundingClientRect();
|
|
isEqualAppunits(
|
|
boundingrect.width,
|
|
borderLeft + paddingLeft + width + paddingRight + borderRight,
|
|
element.id + " bounding rect width"
|
|
);
|
|
isEqualAppunits(
|
|
boundingrect.height,
|
|
borderTop + paddingTop + height + paddingBottom + borderBottom,
|
|
element.id + " bounding rect height"
|
|
);
|
|
isEqualAppunits(
|
|
boundingrect.right - boundingrect.left,
|
|
boundingrect.width,
|
|
element.id + " bounding rect right"
|
|
);
|
|
isEqualAppunits(
|
|
boundingrect.bottom - boundingrect.top,
|
|
boundingrect.height,
|
|
element.id + " bounding rect bottom"
|
|
);
|
|
|
|
var rects = element.getClientRects();
|
|
if (element.id == "div-displaynone" || element.id == "nonappended") {
|
|
is(rects.length, 0, element.id + " getClientRects empty");
|
|
} else {
|
|
is(rects[0].left, boundingrect.left, element.id + " getClientRects left");
|
|
is(rects[0].top, boundingrect.top, element.id + " getClientRects top");
|
|
is(
|
|
rects[0].right,
|
|
boundingrect.right,
|
|
element.id + " getClientRects right"
|
|
);
|
|
is(
|
|
rects[0].bottom,
|
|
boundingrect.bottom,
|
|
element.id + " getClientRects bottom"
|
|
);
|
|
}
|
|
}
|
|
|
|
function checkScrolledElement(element, child) {
|
|
var elemrect = element.getBoundingClientRect();
|
|
var childrect = child.getBoundingClientRect();
|
|
|
|
var topdiff = childrect.top - elemrect.top;
|
|
|
|
element.scrollTop = 20;
|
|
is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll");
|
|
is(element.scrollTop, 20, element.id + " scrollTop after vertical scroll");
|
|
// If the viewport has been transformed, then we might have scrolled to a subpixel value
|
|
// that's slightly different from what we requested. After rounding, however, it should
|
|
// be the same.
|
|
is(
|
|
Math.round(childrect.top - child.getBoundingClientRect().top),
|
|
20,
|
|
"child position after vertical scroll"
|
|
);
|
|
|
|
element.scrollTop = 0;
|
|
is(
|
|
element.scrollLeft,
|
|
0,
|
|
element.id + " scrollLeft after vertical scroll reset"
|
|
);
|
|
is(
|
|
element.scrollTop,
|
|
0,
|
|
element.id + " scrollTop after vertical scroll reset"
|
|
);
|
|
// Scrolling back to the top should work precisely.
|
|
is(
|
|
child.getBoundingClientRect().top,
|
|
childrect.top,
|
|
"child position after vertical scroll reset"
|
|
);
|
|
|
|
element.scrollTop = 10;
|
|
element.scrollTop = -30;
|
|
is(
|
|
element.scrollLeft,
|
|
0,
|
|
element.id + " scrollLeft after vertical scroll negative"
|
|
);
|
|
is(
|
|
element.scrollTop,
|
|
0,
|
|
element.id + " scrollTop after vertical scroll negative"
|
|
);
|
|
is(
|
|
child.getBoundingClientRect().top,
|
|
childrect.top,
|
|
"child position after vertical scroll negative"
|
|
);
|
|
|
|
element.scrollLeft = 18;
|
|
is(
|
|
element.scrollLeft,
|
|
18,
|
|
element.id + " scrollLeft after horizontal scroll"
|
|
);
|
|
is(element.scrollTop, 0, element.id + " scrollTop after horizontal scroll");
|
|
is(
|
|
Math.round(childrect.left - child.getBoundingClientRect().left),
|
|
18,
|
|
"child position after horizontal scroll"
|
|
);
|
|
|
|
element.scrollLeft = -30;
|
|
is(
|
|
element.scrollLeft,
|
|
0,
|
|
element.id + " scrollLeft after horizontal scroll reset"
|
|
);
|
|
is(
|
|
element.scrollTop,
|
|
0,
|
|
element.id + " scrollTop after horizontal scroll reset"
|
|
);
|
|
is(
|
|
child.getBoundingClientRect().left,
|
|
childrect.left,
|
|
"child position after horizontal scroll reset"
|
|
);
|
|
}
|
|
|
|
function checkOffsetState(element, left, top, width, height, parent, testname) {
|
|
checkCoords(element, "offset", left, top, width, height, testname);
|
|
is(element.offsetParent, parent, testname + " offsetParent");
|
|
}
|
|
|
|
function checkScrollState(element, left, top, width, height, testname) {
|
|
checkCoords(element, "scroll", left, top, width, height, testname);
|
|
}
|
|
|
|
function checkClientState(element, left, top, width, height, testname) {
|
|
checkCoords(element, "client", left, top, width, height, testname);
|
|
}
|
|
|
|
function checkCoord(element, type, val, testname) {
|
|
if (val != -10000) {
|
|
is(element[type], Math.round(val), testname + " " + type);
|
|
}
|
|
}
|
|
|
|
function checkCoordFuzzy(element, type, val, fuzz, testname) {
|
|
if (val != -10000) {
|
|
let v = element[type];
|
|
ok(
|
|
Math.abs(v - Math.round(val)) <= fuzz,
|
|
`${testname} ${type}: ${v} vs. ${val}`
|
|
);
|
|
}
|
|
}
|
|
|
|
function checkCoords(element, type, left, top, width, height, testname) {
|
|
checkCoord(element, type + "Left", left, testname);
|
|
checkCoord(element, type + "Top", top, testname);
|
|
|
|
if (type == "scroll") {
|
|
// scrollWidth and scrollHeight can deviate by 1 pixel due to snapping.
|
|
checkCoordFuzzy(element, type + "Width", width, 1, testname);
|
|
checkCoordFuzzy(element, type + "Height", height, 1, testname);
|
|
} else {
|
|
checkCoord(element, type + "Width", width, testname);
|
|
checkCoord(element, type + "Height", height, testname);
|
|
}
|
|
|
|
if (usesSVGLayout(element)) {
|
|
return;
|
|
}
|
|
|
|
if (element.id == "outerpopup" && !element.parentNode.open) {
|
|
// closed popup
|
|
return;
|
|
}
|
|
|
|
if (element.id == "div-displaynone" || element.id == "nonappended") {
|
|
// hidden elements
|
|
ok(
|
|
element[type + "Width"] == 0 && element[type + "Height"] == 0,
|
|
element.id + " has zero " + type + " width and height"
|
|
);
|
|
}
|
|
}
|
|
|
|
function gcs(element, prop) {
|
|
var propVal =
|
|
usesSVGLayout(element) && (prop == "width" || prop == "height")
|
|
? element.getAttribute(prop)
|
|
: getComputedStyle(element, "")[prop];
|
|
if (propVal == "auto" || element.id == "nonappended") {
|
|
return 0;
|
|
}
|
|
return parseFloat(propVal);
|
|
}
|