Skip to content

Commit

Permalink
MathML: Add tests for children not participating to their parent layo…
Browse files Browse the repository at this point in the history
…ut (#18293)

* MathML: Add tests for children not participating to their parent layout

This commit adds new tests to verify that children with "display: none",
"position: absolute" or "position: fixed" don't affect preferred width
calculation or layout of their parents.

- mathml/support/mathml-fragments.js:  Add function to append a non empty
 child (optionally making the element invalid) and use that function for
 forceNonEmptyElement.

- mathml/support/layout-comparison.js: Introduce new helpers function to
  extract the children participating to their parent layout. Modify
  compareLayout so that it browser that list of children instead and allow
  comparison in horizontal+LTR mode.

* Add feature detection otherwise these tests pass when MathML is disabled.

* Add TODO for floats.
  • Loading branch information
fred-wang authored Aug 6, 2019
1 parent a140f23 commit 7a669f4
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Elements not participating to the layout of their parent</title>
<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#layout-algorithms">
<meta name="assert" content="Verify that display: none and out-of-flow positioned elements do not participate to layout of their parent.">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/mathml/support/feature-detection.js"></script>
<script src="/mathml/support/layout-comparison.js"></script>
<script src="/mathml/support/mathml-fragments.js"></script>
<script>
var epsilon = 1;

setup({ explicit_done: true });
window.addEventListener("load", runTests);

function runTests() {

for (tag in MathMLFragments) {
if (!FragmentHelper.isValidChildOfMrow(tag) ||
FragmentHelper.isEmpty(tag))
continue;
// TODO: Add floats too?
["display: none",
"position: absolute",
"position: fixed"
].forEach(style => {
document.body.insertAdjacentHTML("beforeend", `<div style="position: absolute;">\
<div style="display: inline-block"><math>${MathMLFragments[tag]}</math></div>\
<div style="display: inline-block"><math>${MathMLFragments[tag]}</math></div>\
</div>`);
var div = document.body.lastElementChild;

var elementContainer = div.firstElementChild;
var elementContainerWidth = elementContainer.getBoundingClientRect().width;
var element = FragmentHelper.element(elementContainer);
FragmentHelper.forceNonEmptyElement(element);
var allowInvalid = true;
var child = FragmentHelper.appendChild(element, allowInvalid);
child.setAttribute("style", style);

var referenceContainer = div.lastElementChild;
var referenceContainerWidth = referenceContainer.getBoundingClientRect().width;
var reference = FragmentHelper.element(referenceContainer);
FragmentHelper.forceNonEmptyElement(reference);

var epsilon = 1;

test(function() {
// FIXME(fwang): Feature detection should be done per-tag.
assert_true(MathMLFeatureDetection.has_mspace());
assert_approx_equals(elementContainerWidth, referenceContainerWidth, epsilon);
}, `${tag} preferred width calculation is not affected by children with "${style}" style`);

test(function() {
// FIXME(fwang): Feature detection should be done per-tag.
assert_true(MathMLFeatureDetection.has_mspace());
compareLayout(element, reference, epsilon);
}, `${tag} layout is not affected by children with "${style}" style`);

div.style = "display: none;"; // Hide the div after measurement.
});
}

done();
}
</script>
</head>
<body>
<div id="log"></div>
</body>
</html>
46 changes: 28 additions & 18 deletions mathml/support/layout-comparison.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,46 @@ function compareSize(element, reference, epsilon) {
}
}

function compareLayout(element, reference, epsilon) {
if (element.children.length != reference.children.length)
throw "Reference should have the same number of children."
function participateToParentLayout(child) {
var style = window.getComputedStyle(child);
return style.getPropertyValue("display") !== "none" &&
style.getPropertyValue("position") !== "absolute" &&
style.getPropertyValue("position") !== "fixed";
}

function childrenParticipatingToLayout(element) {
var children = [];
Array.from(element.children).forEach(child => {
if (participateToParentLayout(child))
children.push(child);
})
return children;
}

function compareLayout(element, reference, epsilon) {
// Compare sizes of elements and children.
var param = getWritingMode(element, reference);

compareSize(element, reference, epsilon);
var elementBox = element.getBoundingClientRect();
var referenceBox = reference.getBoundingClientRect();
for (var i = 0; i < element.children.length; i++) {
var childDisplay = window.
getComputedStyle(element.children[i]).getPropertyValue("display");
var referenceChildDisplay = window.
getComputedStyle(reference.children[i]).getPropertyValue("display");
if (referenceChildDisplay !== childDisplay)
throw "compareLayout: children of reference should have the same display values.";
if (childDisplay === "none")
continue;

compareSize(element.children[i], reference.children[i], epsilon);
var elementChildren = childrenParticipatingToLayout(element);
var referenceChildren = childrenParticipatingToLayout(reference);
if (elementChildren.length != referenceChildren.length)
throw "Reference should have the same number of children participating to layout."

for (var i = 0; i < elementChildren.length; i++) {
compareSize(elementChildren[i], referenceChildren[i], epsilon);

var childBox = element.children[i].getBoundingClientRect();
var referenceChildBox = reference.children[i].getBoundingClientRect();
var childBox = elementChildren[i].getBoundingClientRect();
var referenceChildBox = referenceChildren[i].getBoundingClientRect();

switch(param.mode) {
case "horizontal-tb":
if (!param.rtl)
throw "compareLayout: unexpected writing-mode value";
assert_approx_equals(elementBox.right - childBox.right,
assert_approx_equals(param.rtl ?
elementBox.right - childBox.right :
childBox.left - elementBox.left,
referenceChildBox.left - referenceBox.left,
epsilon,
`inline position (child ${i})`);
Expand Down
20 changes: 14 additions & 6 deletions mathml/support/mathml-fragments.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,25 @@ var FragmentHelper = {
return fragment.getElementsByClassName('element')[0];
},

forceNonEmptyElement: function(fragment) {
appendChild: function(fragment, allowInvalid) {
var element = this.element(fragment) || fragment;
if (element.firstElementChild)
return element.firstElementChild;
if (element.classList.contains("mathml-container"))
return element.appendChild(this.createElement("mrow"));
if (element.classList.contains("foreign-container")) {
var el = document.createElement("span");
el.textContent = "a";
return element.appendChild(el);
}
throw "Cannot make the element nonempty";
if (element.classList.contains("mathml-container") || allowInvalid) {
var el = this.createElement("mi");
el.textContent = "a";
return element.appendChild(el);
}
throw "Cannot append child to the element";
},

forceNonEmptyElement: function(fragment) {
var element = this.element(fragment) || fragment;
if (element.firstElementChild)
return element.firstElementChild;
return this.appendChild(fragment);
}
}

0 comments on commit 7a669f4

Please sign in to comment.