This patch implements support for the width/height properties on MathML elements [1]. The general algorithm from the spec is as follows: (1) The outcome of the math layout is a "math content box". (2) The content box sets its size from computed width/height values. If auto, it's the one of the "math content box". This patch ignores percentage values for now [2] [3]. (3) math content box is shifted so that its inline-start and top edges aligns with the ones of the content box. There are exceptions elements like mfrac and munder/mover/munderover which instead horizontally center the math content box within the content box. For baseline adjustment, we follow what Chromium does, see [4]. (4) Padding+border are added around the content box. Note that we ignore the box-sizing property for now [5]. The patch essentially tweaks the various MathML layout algorithms to perform steps (3) and (4) before the calls to GetBorderPaddingForPlace and InflateReflowAndBoundingMetrics. [1] https://w3c.github.io/mathml-core/#layout-algorithms [2] https://github.com/w3c/mathml-core/issues/76 [3] https://github.com/w3c/mathml-core/issues/77 [4] https://github.com/w3c/mathml-core/issues/259 [5] https://github.com/w3c/mathml-core/issues/257 Below is more information about test coverage: - width-height-001: Verify that width, height, inline-size and block-size properties sets the size of the content box. This test used to verify they are ignored, this patch fixes the `<meta name="assert">` tag. It also adds a test for the case the specified size is smaller than the content (we force non empty descendants to make sure this content is large enough) and to verify the width is used for the preferred width. - width-height-002, width-height-003: These are reftests visually checking offsets of the math content box within a larger content box (specified by width/height) for the mtext, mrow, mpadded, mfrac, msqrt, mroot, in LTR/RTL modes. In particular they allow to verify some painted elements like fraction bar and radical symbols. - width-height-004: This test more directly checks that the math content box is horizontally centered within a larger content box for munder, mover, munderover and mfrac. This patch extends the test to cover the case when the math content box is wider (i.e. overflowing outside the content box) and removes unnecessary specified height. - width-height-005: New test for other layout algorithm that don't center the math content box, checking inline-start edges of children when a width is specified. We check both LTR/RTL modes and wider/narrower content boxes. - width-height-006: Same but checking the top edges for larger/smaller height and verifying that baseline is perserved. Differential Revision: https://phabricator.services.mozilla.com/D221436
128 lines
4.9 KiB
C++
128 lines
4.9 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsMathMLmspaceFrame.h"
|
|
|
|
#include "mozilla/dom/MathMLElement.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include <algorithm>
|
|
|
|
using namespace mozilla;
|
|
|
|
//
|
|
// <mspace> -- space - implementation
|
|
//
|
|
|
|
nsIFrame* NS_NewMathMLmspaceFrame(PresShell* aPresShell,
|
|
ComputedStyle* aStyle) {
|
|
return new (aPresShell)
|
|
nsMathMLmspaceFrame(aStyle, aPresShell->GetPresContext());
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmspaceFrame)
|
|
|
|
nsMathMLmspaceFrame::~nsMathMLmspaceFrame() = default;
|
|
|
|
nsresult nsMathMLmspaceFrame::AttributeChanged(int32_t aNameSpaceID,
|
|
nsAtom* aAttribute,
|
|
int32_t aModType) {
|
|
if (aNameSpaceID == kNameSpaceID_None) {
|
|
bool hasDirtyAttributes = false;
|
|
IntrinsicDirty intrinsicDirty = IntrinsicDirty::None;
|
|
if (aAttribute == nsGkAtoms::width) {
|
|
mWidth.mState = Attribute::ParsingState::Dirty;
|
|
hasDirtyAttributes = true;
|
|
intrinsicDirty = IntrinsicDirty::FrameAndAncestors;
|
|
} else if (aAttribute == nsGkAtoms::height) {
|
|
mHeight.mState = Attribute::ParsingState::Dirty;
|
|
hasDirtyAttributes = true;
|
|
} else if (aAttribute == nsGkAtoms::depth_) {
|
|
mDepth.mState = Attribute::ParsingState::Dirty;
|
|
hasDirtyAttributes = true;
|
|
}
|
|
if (hasDirtyAttributes) {
|
|
PresShell()->FrameNeedsReflow(this, intrinsicDirty, NS_FRAME_IS_DIRTY);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
|
|
aModType);
|
|
}
|
|
|
|
nscoord nsMathMLmspaceFrame::CalculateAttributeValue(nsAtom* aAtom,
|
|
Attribute& aAttribute,
|
|
uint32_t aFlags,
|
|
float aFontSizeInflation) {
|
|
if (aAttribute.mState == Attribute::ParsingState::Dirty) {
|
|
nsAutoString value;
|
|
aAttribute.mState = Attribute::ParsingState::Invalid;
|
|
mContent->AsElement()->GetAttr(aAtom, value);
|
|
if (!value.IsEmpty()) {
|
|
if (dom::MathMLElement::ParseNumericValue(
|
|
value, aAttribute.mValue, aFlags, PresContext()->Document())) {
|
|
aAttribute.mState = Attribute::ParsingState::Valid;
|
|
} else {
|
|
ReportParseError(aAtom->GetUTF16String(), value.get());
|
|
}
|
|
}
|
|
}
|
|
// Invalid is interpreted as the default which is 0.
|
|
// Percentages are interpreted as a multiple of the default value.
|
|
if (aAttribute.mState == Attribute::ParsingState::Invalid ||
|
|
aAttribute.mValue.GetUnit() == eCSSUnit_Percent) {
|
|
return 0;
|
|
}
|
|
return CalcLength(PresContext(), mComputedStyle, aAttribute.mValue,
|
|
aFontSizeInflation);
|
|
}
|
|
|
|
nsresult nsMathMLmspaceFrame::Place(DrawTarget* aDrawTarget,
|
|
const PlaceFlags& aFlags,
|
|
ReflowOutput& aDesiredSize) {
|
|
float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
|
|
|
|
// <mspace/> is listed among MathML elements allowing negative spacing and
|
|
// the MathML test suite contains "Presentation/TokenElements/mspace/mspace2"
|
|
// as an example. Hence we allow negative values.
|
|
nscoord width = CalculateAttributeValue(
|
|
nsGkAtoms::width, mWidth, dom::MathMLElement::PARSE_ALLOW_NEGATIVE,
|
|
fontSizeInflation);
|
|
|
|
// We do not allow negative values for height and depth attributes. See bug
|
|
// 716349.
|
|
nscoord height =
|
|
CalculateAttributeValue(nsGkAtoms::height, mHeight, 0, fontSizeInflation);
|
|
nscoord depth =
|
|
CalculateAttributeValue(nsGkAtoms::depth_, mDepth, 0, fontSizeInflation);
|
|
|
|
mBoundingMetrics = nsBoundingMetrics();
|
|
mBoundingMetrics.width = width;
|
|
mBoundingMetrics.ascent = height;
|
|
mBoundingMetrics.descent = depth;
|
|
mBoundingMetrics.leftBearing = 0;
|
|
mBoundingMetrics.rightBearing = mBoundingMetrics.width;
|
|
|
|
aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent);
|
|
aDesiredSize.Width() = std::max(0, mBoundingMetrics.width);
|
|
aDesiredSize.Height() = mBoundingMetrics.ascent + mBoundingMetrics.descent;
|
|
// Also return our bounding metrics
|
|
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
|
|
|
|
// Apply width/height to math content box.
|
|
const PlaceFlags flags;
|
|
auto sizes = GetWidthAndHeightForPlaceAdjustment(flags);
|
|
ApplyAdjustmentForWidthAndHeight(flags, sizes, aDesiredSize,
|
|
mBoundingMetrics);
|
|
|
|
// Add padding+border.
|
|
auto borderPadding = GetBorderPaddingForPlace(aFlags);
|
|
InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize,
|
|
mBoundingMetrics);
|
|
return NS_OK;
|
|
}
|