Commit 858aea82 authored by Rob Buis's avatar Rob Buis Committed by Commit Bot

[mathml] Implement movablelimits

If the <munder>, <mover> or <munderover> elements have a computed
math-style property equal to compact and their base is an embellished
operator with the movablelimits property, then their layout algorithms
are respectively the same as the ones described for <msub>, <msup> and
<msubsup>. In this CL we delegate to the scripts algorithm which is
changed to be able to handle script types over, under and underover.

This CL does not handle embellished operators as defined [2] but just
the first case, i.e. <mo> elements but not the two other cases, to
simplify this CL.

To handle dynamic changes to the movablelimits attribute on <mo>
UpdateFromElement is implemented on LayoutNGMathMLBlockFlow.

To handle dynamic changes to attributes (like displaystyle) on math
elements UpdateFromElement is implemented on LayoutNGMathMLBlock.

[1] https://mathml-refresh.github.io/mathml-core/#children-of-munder-mover-munderover
[2] https://mathml-refresh.github.io/mathml-core/#embellished-operators

Bug: 6606
Change-Id: Ib4af58ed88b7d41944f4d4676bba812c3b7a3a2e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2383023Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: default avatarFrédéric Wang <fwang@igalia.com>
Commit-Queue: Rob Buis <rbuis@igalia.com>
Cr-Commit-Position: refs/heads/master@{#804331}
parent aaf48338
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/layout/layout_analyzer.h" #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/mathml/mathml_element.h" #include "third_party/blink/renderer/core/mathml/mathml_element.h"
#include "third_party/blink/renderer/core/mathml/mathml_under_over_element.h"
namespace blink { namespace blink {
...@@ -43,4 +44,16 @@ bool LayoutNGMathMLBlock::CanHaveChildren() const { ...@@ -43,4 +44,16 @@ bool LayoutNGMathMLBlock::CanHaveChildren() const {
return LayoutNGMixin<LayoutBlock>::CanHaveChildren(); return LayoutNGMixin<LayoutBlock>::CanHaveChildren();
} }
void LayoutNGMathMLBlock::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) {
LayoutNGMixin<LayoutBlock>::StyleDidChange(diff, old_style);
if (!old_style)
return;
if (IsA<MathMLUnderOverElement>(GetNode()) &&
old_style->MathStyle() != StyleRef().MathStyle()) {
SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
} // namespace blink } // namespace blink
...@@ -21,6 +21,7 @@ class LayoutNGMathMLBlock : public LayoutNGMixin<LayoutBlock> { ...@@ -21,6 +21,7 @@ class LayoutNGMathMLBlock : public LayoutNGMixin<LayoutBlock> {
bool IsOfType(LayoutObjectType) const final; bool IsOfType(LayoutObjectType) const final;
bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const final; bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const final;
bool CanHaveChildren() const final; bool CanHaveChildren() const final;
void StyleDidChange(StyleDifference, const ComputedStyle*) final;
}; };
} // namespace blink } // namespace blink
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
#include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h" #include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h"
#include "third_party/blink/renderer/core/mathml/mathml_operator_element.h"
#include "third_party/blink/renderer/core/mathml/mathml_radical_element.h" #include "third_party/blink/renderer/core/mathml/mathml_radical_element.h"
#include "third_party/blink/renderer/core/mathml/mathml_scripts_element.h" #include "third_party/blink/renderer/core/mathml/mathml_scripts_element.h"
...@@ -198,6 +199,22 @@ MinMaxSizes GetMinMaxSizesForVerticalStretchyOperator( ...@@ -198,6 +199,22 @@ MinMaxSizes GetMinMaxSizesForVerticalStretchyOperator(
return sizes; return sizes;
} }
bool IsUnderOverLaidOutAsSubSup(const NGBlockNode& node) {
DCHECK(IsValidMathMLScript(node));
if (HasDisplayStyle(node.Style()))
return false;
if (!node.IsBlock() || !node.GetLayoutBox()->IsMathML())
return false;
auto base = To<NGBlockNode>(FirstChildInFlow(node));
// TODO(crbug.com/1124298)):
// https://mathml-refresh.github.io/mathml-core/#embellished-operators
if (auto* element =
DynamicTo<MathMLOperatorElement>(base.GetLayoutBox()->GetNode())) {
return element->HasBooleanProperty(MathMLOperatorElement::kMovableLimits);
}
return false;
}
namespace { namespace {
inline LayoutUnit DefaultFractionLineThickness(const ComputedStyle& style) { inline LayoutUnit DefaultFractionLineThickness(const ComputedStyle& style) {
......
...@@ -80,6 +80,7 @@ RadicalVerticalParameters GetRadicalVerticalParameters(const ComputedStyle&, ...@@ -80,6 +80,7 @@ RadicalVerticalParameters GetRadicalVerticalParameters(const ComputedStyle&,
MinMaxSizes GetMinMaxSizesForVerticalStretchyOperator(const ComputedStyle&, MinMaxSizes GetMinMaxSizesForVerticalStretchyOperator(const ComputedStyle&,
UChar character); UChar character);
bool IsUnderOverLaidOutAsSubSup(const NGBlockNode& node);
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_LAYOUT_UTILS_H_ #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_LAYOUT_UTILS_H_
...@@ -121,6 +121,7 @@ void NGMathScriptsLayoutAlgorithm::GatherChildren( ...@@ -121,6 +121,7 @@ void NGMathScriptsLayoutAlgorithm::GatherChildren(
} }
switch (script_type) { switch (script_type) {
case MathScriptType::kSub: case MathScriptType::kSub:
case MathScriptType::kUnder:
// These elements must have exactly two children. // These elements must have exactly two children.
// The second child is a postscript and there are no prescripts. // The second child is a postscript and there are no prescripts.
// <msub> base subscript </msub> // <msub> base subscript </msub>
...@@ -129,9 +130,11 @@ void NGMathScriptsLayoutAlgorithm::GatherChildren( ...@@ -129,9 +130,11 @@ void NGMathScriptsLayoutAlgorithm::GatherChildren(
sub_sup_pairs->at(0).sub = block_child; sub_sup_pairs->at(0).sub = block_child;
continue; continue;
case MathScriptType::kSuper: case MathScriptType::kSuper:
case MathScriptType::kOver:
DCHECK(!sub_sup_pairs->at(0).sup); DCHECK(!sub_sup_pairs->at(0).sup);
sub_sup_pairs->at(0).sup = block_child; sub_sup_pairs->at(0).sup = block_child;
continue; continue;
case MathScriptType::kUnderOver:
case MathScriptType::kSubSup: case MathScriptType::kSubSup:
// These elements must have exactly three children. // These elements must have exactly three children.
// The second and third children are postscripts and there are no // The second and third children are postscripts and there are no
...@@ -184,6 +187,7 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics( ...@@ -184,6 +187,7 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics(
MathScriptType type = Node().ScriptType(); MathScriptType type = Node().ScriptType();
if (type == MathScriptType::kSub || type == MathScriptType::kSubSup || if (type == MathScriptType::kSub || type == MathScriptType::kSubSup ||
type == MathScriptType::kMultiscripts || type == MathScriptType::kUnder ||
type == MathScriptType::kMultiscripts) { type == MathScriptType::kMultiscripts) {
metrics.sub_shift = metrics.sub_shift =
std::max(parameters.subscript_shift_down, std::max(parameters.subscript_shift_down,
...@@ -191,6 +195,7 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics( ...@@ -191,6 +195,7 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics(
} }
LayoutUnit shift_up = parameters.superscript_shift_up; LayoutUnit shift_up = parameters.superscript_shift_up;
if (type == MathScriptType::kSuper || type == MathScriptType::kSubSup || if (type == MathScriptType::kSuper || type == MathScriptType::kSubSup ||
type == MathScriptType::kMultiscripts || type == MathScriptType::kOver ||
type == MathScriptType::kMultiscripts) { type == MathScriptType::kMultiscripts) {
if (Style().MathSuperscriptShiftStyle() == if (Style().MathSuperscriptShiftStyle() ==
EMathSuperscriptShiftStyle::kInline) EMathSuperscriptShiftStyle::kInline)
...@@ -201,19 +206,22 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics( ...@@ -201,19 +206,22 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics(
} }
switch (type) { switch (type) {
case MathScriptType::kSub: { case MathScriptType::kSub:
case MathScriptType::kUnder: {
metrics.descent = sub_metrics[0].descent; metrics.descent = sub_metrics[0].descent;
metrics.sub_shift = metrics.sub_shift =
std::max(metrics.sub_shift, std::max(metrics.sub_shift,
sub_metrics[0].ascent - parameters.subscript_top_max); sub_metrics[0].ascent - parameters.subscript_top_max);
} break; } break;
case MathScriptType::kSuper: { case MathScriptType::kSuper:
case MathScriptType::kOver: {
metrics.ascent = sup_metrics[0].ascent; metrics.ascent = sup_metrics[0].ascent;
metrics.sup_shift = metrics.sup_shift =
std::max(metrics.sup_shift, std::max(metrics.sup_shift,
parameters.superscript_bottom_min + sup_metrics[0].descent); parameters.superscript_bottom_min + sup_metrics[0].descent);
} break; } break;
case MathScriptType::kMultiscripts: case MathScriptType::kMultiscripts:
case MathScriptType::kUnderOver:
case MathScriptType::kSubSup: { case MathScriptType::kSubSup: {
for (wtf_size_t idx = 0; idx < sub_metrics.size(); ++idx) { for (wtf_size_t idx = 0; idx < sub_metrics.size(); ++idx) {
metrics.ascent = std::max(metrics.ascent, sup_metrics[idx].ascent); metrics.ascent = std::max(metrics.ascent, sup_metrics[idx].ascent);
...@@ -255,12 +263,6 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics( ...@@ -255,12 +263,6 @@ NGMathScriptsLayoutAlgorithm::GetVerticalMetrics(
metrics.sup_shift = std::max(metrics.sup_shift, sup_script_shift); metrics.sup_shift = std::max(metrics.sup_shift, sup_script_shift);
} }
} break; } break;
case MathScriptType::kOver:
case MathScriptType::kUnder:
case MathScriptType::kUnderOver:
// TODO(rbuis): implement movablelimits.
NOTREACHED();
break;
} }
return metrics; return metrics;
...@@ -431,6 +433,8 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes( ...@@ -431,6 +433,8 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes(
LayoutUnit space = GetSpaceAfterScript(Style()); LayoutUnit space = GetSpaceAfterScript(Style());
switch (Node().ScriptType()) { switch (Node().ScriptType()) {
case MathScriptType::kSub: case MathScriptType::kSub:
case MathScriptType::kUnder:
case MathScriptType::kOver:
case MathScriptType::kSuper: { case MathScriptType::kSuper: {
// TODO(fwang): Take italic correction into account. // TODO(fwang): Take italic correction into account.
NGBlockNode sub = sub_sup_pairs[0].sub; NGBlockNode sub = sub_sup_pairs[0].sub;
...@@ -448,6 +452,7 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes( ...@@ -448,6 +452,7 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes(
break; break;
} }
case MathScriptType::kSubSup: case MathScriptType::kSubSup:
case MathScriptType::kUnderOver:
case MathScriptType::kMultiscripts: { case MathScriptType::kMultiscripts: {
// TODO(fwang): Take italic correction into account. // TODO(fwang): Take italic correction into account.
MinMaxSizes sub_sup_pair_size; MinMaxSizes sub_sup_pair_size;
...@@ -478,12 +483,6 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes( ...@@ -478,12 +483,6 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes(
} while (++index < sub_sup_pairs.size()); } while (++index < sub_sup_pairs.size());
break; break;
} }
case MathScriptType::kUnder:
case MathScriptType::kOver:
case MathScriptType::kUnderOver:
// TODO(rbuis): implement movablelimits.
NOTREACHED();
break;
} }
sizes += BorderScrollbarPadding().InlineSum(); sizes += BorderScrollbarPadding().InlineSum();
......
...@@ -136,8 +136,8 @@ NOINLINE void DetermineMathMLAlgorithmAndRun( ...@@ -136,8 +136,8 @@ NOINLINE void DetermineMathMLAlgorithmAndRun(
return; return;
} else if (IsA<MathMLScriptsElement>(element) && } else if (IsA<MathMLScriptsElement>(element) &&
IsValidMathMLScript(params.node)) { IsValidMathMLScript(params.node)) {
// TODO(rbuis): take into account movablelimits. if (IsA<MathMLUnderOverElement>(element) &&
if (IsA<MathMLUnderOverElement>(element)) { !IsUnderOverLaidOutAsSubSup(params.node)) {
CreateAlgorithmAndRun<NGMathUnderOverLayoutAlgorithm>(params, callback); CreateAlgorithmAndRun<NGMathUnderOverLayoutAlgorithm>(params, callback);
} else { } else {
CreateAlgorithmAndRun<NGMathScriptsLayoutAlgorithm>(params, callback); CreateAlgorithmAndRun<NGMathScriptsLayoutAlgorithm>(params, callback);
......
...@@ -128,8 +128,11 @@ void MathMLOperatorElement::ParseAttribute( ...@@ -128,8 +128,11 @@ void MathMLOperatorElement::ParseAttribute(
SetOperatorPropertyDirtyFlagIfNeeded( SetOperatorPropertyDirtyFlagIfNeeded(
param, MathMLOperatorElement::kMovableLimits, needs_layout); param, MathMLOperatorElement::kMovableLimits, needs_layout);
} }
if (needs_layout && GetLayoutObject()) if (needs_layout && GetLayoutObject() && GetLayoutObject()->IsMathML()) {
GetLayoutObject()->UpdateFromElement(); GetLayoutObject()
->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
MathMLElement::ParseAttribute(param); MathMLElement::ParseAttribute(param);
} }
......
...@@ -1249,10 +1249,7 @@ crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-axis-height- ...@@ -1249,10 +1249,7 @@ crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-axis-height-
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-001.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-002.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-001.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-001.html [ Failure ]
crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-002.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-002.html [ Failure ]
...@@ -1262,9 +1259,6 @@ crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-002.html [ F ...@@ -1262,9 +1259,6 @@ crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-002.html [ F
crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-axis-height.html [ Failure ] crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-axis-height.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/attribute-mapping-002.html [ Failure ] crbug.com/6606 external/wpt/mathml/relations/css-styling/attribute-mapping-002.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-011.html [ Failure ] crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-011.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-012.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-013.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-015.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-1.html [ Failure ] crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-1.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-2.html [ Failure ] crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-2.html [ Failure ]
crbug.com/6606 external/wpt/mathml/relations/css-styling/ignored-properties-001.html [ Failure Timeout ] crbug.com/6606 external/wpt/mathml/relations/css-styling/ignored-properties-001.html [ Failure Timeout ]
...@@ -1295,7 +1289,6 @@ crbug.com/6606 external/wpt/mathml/relations/html5-tree/integration-point-4.html ...@@ -1295,7 +1289,6 @@ crbug.com/6606 external/wpt/mathml/relations/html5-tree/integration-point-4.html
# Need to handle OOF for these. # Need to handle OOF for these.
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/embellished-operator-001.html [ Skip ] crbug.com/6606 external/wpt/mathml/presentation-markup/operators/embellished-operator-001.html [ Skip ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/embellished-operator-002.html [ Skip ] crbug.com/6606 external/wpt/mathml/presentation-markup/operators/embellished-operator-002.html [ Skip ]
crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits-from-in-flow.html [ Skip ]
crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/cramped-001.html [ Skip ] crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/cramped-001.html [ Skip ]
crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-004.html [ Skip ] crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-004.html [ Skip ]
......
This is a testharness.js-based test.
FAIL Operator dictionary chunk 1 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 2 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 3 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 4 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 5 - movablelimits assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Operator dictionary chunk 1 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 2 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 3 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 4 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 5 - movablelimits assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Operator dictionary chunk 1 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 2 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 3 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 4 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 5 - movablelimits assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Operator dictionary chunk 1 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 2 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 3 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 4 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 5 - movablelimits assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Operator dictionary chunk 1 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 2 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 3 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 4 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 5 - movablelimits assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Operator dictionary chunk 1 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 2 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 3 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 4 - movablelimits assert_true: expected true got false
FAIL Operator dictionary chunk 5 - movablelimits assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL movablelimits for munder element (displaystyle=false) assert_true: expected true got false
FAIL movablelimits for munder element (displaystyle=true) assert_true: expected true got false
FAIL movablelimits for mover element (displaystyle=false) assert_true: expected true got false
FAIL movablelimits for mover element (displaystyle=true) assert_true: expected true got false
FAIL movablelimits for munderover element (displaystyle=false) assert_true: expected true got false
FAIL movablelimits for munderover element (displaystyle=true) assert_true: expected true got false
Harness: the test ran to completion.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment