123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "nsMathMLmpaddedFrame.h"
- #include "nsMathMLElement.h"
- #include "mozilla/gfx/2D.h"
- #include <algorithm>
- //
- // <mpadded> -- adjust space around content - implementation
- //
- #define NS_MATHML_SIGN_INVALID -1 // if the attribute is not there
- #define NS_MATHML_SIGN_UNSPECIFIED 0
- #define NS_MATHML_SIGN_MINUS 1
- #define NS_MATHML_SIGN_PLUS 2
- #define NS_MATHML_PSEUDO_UNIT_UNSPECIFIED 0
- #define NS_MATHML_PSEUDO_UNIT_ITSELF 1 // special
- #define NS_MATHML_PSEUDO_UNIT_WIDTH 2
- #define NS_MATHML_PSEUDO_UNIT_HEIGHT 3
- #define NS_MATHML_PSEUDO_UNIT_DEPTH 4
- #define NS_MATHML_PSEUDO_UNIT_NAMEDSPACE 5
- nsIFrame*
- NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
- {
- return new (aPresShell) nsMathMLmpaddedFrame(aContext);
- }
- NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmpaddedFrame)
- nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame()
- {
- }
- NS_IMETHODIMP
- nsMathMLmpaddedFrame::InheritAutomaticData(nsIFrame* aParent)
- {
- // let the base class get the default from our parent
- nsMathMLContainerFrame::InheritAutomaticData(aParent);
- mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
- return NS_OK;
- }
- void
- nsMathMLmpaddedFrame::ProcessAttributes()
- {
- /*
- parse the attributes
- width = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
- height = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
- depth = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
- lspace = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
- voffset= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
- */
- nsAutoString value;
- // width
- mWidthSign = NS_MATHML_SIGN_INVALID;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value);
- if (!value.IsEmpty()) {
- if (!ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit)) {
- ReportParseError(nsGkAtoms::width->GetUTF16String(), value.get());
- }
- }
- // height
- mHeightSign = NS_MATHML_SIGN_INVALID;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value);
- if (!value.IsEmpty()) {
- if (!ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit)) {
- ReportParseError(nsGkAtoms::height->GetUTF16String(), value.get());
- }
- }
- // depth
- mDepthSign = NS_MATHML_SIGN_INVALID;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::depth_, value);
- if (!value.IsEmpty()) {
- if (!ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit)) {
- ReportParseError(nsGkAtoms::depth_->GetUTF16String(), value.get());
- }
- }
- // lspace
- mLeadingSpaceSign = NS_MATHML_SIGN_INVALID;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
- if (!value.IsEmpty()) {
- if (!ParseAttribute(value, mLeadingSpaceSign, mLeadingSpace,
- mLeadingSpacePseudoUnit)) {
- ReportParseError(nsGkAtoms::lspace_->GetUTF16String(), value.get());
- }
- }
- // voffset
- mVerticalOffsetSign = NS_MATHML_SIGN_INVALID;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::voffset_, value);
- if (!value.IsEmpty()) {
- if (!ParseAttribute(value, mVerticalOffsetSign, mVerticalOffset,
- mVerticalOffsetPseudoUnit)) {
- ReportParseError(nsGkAtoms::voffset_->GetUTF16String(), value.get());
- }
- }
-
- }
- // parse an input string in the following format (see bug 148326 for testcases):
- // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
- bool
- nsMathMLmpaddedFrame::ParseAttribute(nsString& aString,
- int32_t& aSign,
- nsCSSValue& aCSSValue,
- int32_t& aPseudoUnit)
- {
- aCSSValue.Reset();
- aSign = NS_MATHML_SIGN_INVALID;
- aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED;
- aString.CompressWhitespace(); // aString is not a const in this code
- int32_t stringLength = aString.Length();
- if (!stringLength)
- return false;
- nsAutoString number, unit;
- //////////////////////
- // see if the sign is there
- int32_t i = 0;
- if (aString[0] == '+') {
- aSign = NS_MATHML_SIGN_PLUS;
- i++;
- }
- else if (aString[0] == '-') {
- aSign = NS_MATHML_SIGN_MINUS;
- i++;
- }
- else
- aSign = NS_MATHML_SIGN_UNSPECIFIED;
- // get the number
- bool gotDot = false, gotPercent = false;
- for (; i < stringLength; i++) {
- char16_t c = aString[i];
- if (gotDot && c == '.') {
- // error - two dots encountered
- aSign = NS_MATHML_SIGN_INVALID;
- return false;
- }
- if (c == '.')
- gotDot = true;
- else if (!nsCRT::IsAsciiDigit(c)) {
- break;
- }
- number.Append(c);
- }
- // catch error if we didn't enter the loop above... we could simply initialize
- // floatValue = 1, to cater for cases such as width="height", but that wouldn't
- // be in line with the spec which requires an explicit number
- if (number.IsEmpty()) {
- aSign = NS_MATHML_SIGN_INVALID;
- return false;
- }
- nsresult errorCode;
- float floatValue = number.ToFloat(&errorCode);
- if (NS_FAILED(errorCode)) {
- aSign = NS_MATHML_SIGN_INVALID;
- return false;
- }
- // see if this is a percentage-based value
- if (i < stringLength && aString[i] == '%') {
- i++;
- gotPercent = true;
- }
- // the remainder now should be a css-unit, or a pseudo-unit, or a named-space
- aString.Right(unit, stringLength - i);
- if (unit.IsEmpty()) {
- if (gotPercent) {
- // case ["+"|"-"] unsigned-number "%"
- aCSSValue.SetPercentValue(floatValue / 100.0f);
- aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
- return true;
- } else {
- // case ["+"|"-"] unsigned-number
- // XXXfredw: should we allow non-zero unitless values? See bug 757703.
- if (!floatValue) {
- aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
- aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
- return true;
- }
- }
- }
- else if (unit.EqualsLiteral("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH;
- else if (unit.EqualsLiteral("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT;
- else if (unit.EqualsLiteral("depth")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH;
- else if (!gotPercent) { // percentage can only apply to a pseudo-unit
- // see if the unit is a named-space
- if (nsMathMLElement::ParseNamedSpaceValue(unit, aCSSValue,
- nsMathMLElement::
- PARSE_ALLOW_NEGATIVE)) {
- // re-scale properly, and we know that the unit of the named-space is 'em'
- floatValue *= aCSSValue.GetFloatValue();
- aCSSValue.SetFloatValue(floatValue, eCSSUnit_EM);
- aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE;
- return true;
- }
- // see if the input was just a CSS value
- // We are not supposed to have a unitless, percent, negative or namedspace
- // value here.
- number.Append(unit); // leave the sign out if it was there
- if (nsMathMLElement::ParseNumericValue(number, aCSSValue,
- nsMathMLElement::
- PARSE_SUPPRESS_WARNINGS, nullptr))
- return true;
- }
- // if we enter here, we have a number that will act as a multiplier on a pseudo-unit
- if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) {
- if (gotPercent)
- aCSSValue.SetPercentValue(floatValue / 100.0f);
- else
- aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
- return true;
- }
- #ifdef DEBUG
- printf("mpadded: attribute with bad numeric value: %s\n",
- NS_LossyConvertUTF16toASCII(aString).get());
- #endif
- // if we reach here, it means we encounter an unexpected input
- aSign = NS_MATHML_SIGN_INVALID;
- return false;
- }
- void
- nsMathMLmpaddedFrame::UpdateValue(int32_t aSign,
- int32_t aPseudoUnit,
- const nsCSSValue& aCSSValue,
- const ReflowOutput& aDesiredSize,
- nscoord& aValueToUpdate,
- float aFontSizeInflation) const
- {
- nsCSSUnit unit = aCSSValue.GetUnit();
- if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) {
- nscoord scaler = 0, amount = 0;
- if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) {
- switch(aPseudoUnit) {
- case NS_MATHML_PSEUDO_UNIT_WIDTH:
- scaler = aDesiredSize.Width();
- break;
- case NS_MATHML_PSEUDO_UNIT_HEIGHT:
- scaler = aDesiredSize.BlockStartAscent();
- break;
- case NS_MATHML_PSEUDO_UNIT_DEPTH:
- scaler = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
- break;
- default:
- // if we ever reach here, it would mean something is wrong
- // somewhere with the setup and/or the caller
- NS_ERROR("Unexpected Pseudo Unit");
- return;
- }
- }
- if (eCSSUnit_Number == unit)
- amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue());
- else if (eCSSUnit_Percent == unit)
- amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue());
- else
- amount = CalcLength(PresContext(), mStyleContext, aCSSValue,
- aFontSizeInflation);
- if (NS_MATHML_SIGN_PLUS == aSign)
- aValueToUpdate += amount;
- else if (NS_MATHML_SIGN_MINUS == aSign)
- aValueToUpdate -= amount;
- else
- aValueToUpdate = amount;
- }
- }
- void
- nsMathMLmpaddedFrame::Reflow(nsPresContext* aPresContext,
- ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput,
- nsReflowStatus& aStatus)
- {
- mPresentationData.flags &= ~NS_MATHML_ERROR;
- ProcessAttributes();
- ///////////////
- // Let the base class format our content like an inferred mrow
- nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
- aReflowInput, aStatus);
- //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
- }
- /* virtual */ nsresult
- nsMathMLmpaddedFrame::Place(DrawTarget* aDrawTarget,
- bool aPlaceOrigin,
- ReflowOutput& aDesiredSize)
- {
- nsresult rv =
- nsMathMLContainerFrame::Place(aDrawTarget, false, aDesiredSize);
- if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
- DidReflowChildren(PrincipalChildList().FirstChild());
- return rv;
- }
- nscoord height = aDesiredSize.BlockStartAscent();
- nscoord depth = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
- // The REC says:
- //
- // "The lspace attribute ('leading' space) specifies the horizontal location
- // of the positioning point of the child content with respect to the
- // positioning point of the mpadded element. By default they coincide, and
- // therefore absolute values for lspace have the same effect as relative
- // values."
- //
- // "MathML renderers should ensure that, except for the effects of the
- // attributes, the relative spacing between the contents of the mpadded
- // element and surrounding MathML elements would not be modified by replacing
- // an mpadded element with an mrow element with the same content, even if
- // linebreaking occurs within the mpadded element."
- //
- // (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded)
- //
- // "In those discussions, the terms leading and trailing are used to specify
- // a side of an object when which side to use depends on the directionality;
- // ie. leading means left in LTR but right in RTL."
- // (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math)
- nscoord lspace = 0;
- // In MathML3, "width" will be the bounding box width and "advancewidth" will
- // refer "to the horizontal distance between the positioning point of the
- // mpadded and the positioning point for the following content". MathML2
- // doesn't make the distinction.
- nscoord width = aDesiredSize.Width();
- nscoord voffset = 0;
- int32_t pseudoUnit;
- nscoord initialWidth = width;
- float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
- // update width
- pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
- ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit;
- UpdateValue(mWidthSign, pseudoUnit, mWidth,
- aDesiredSize, width, fontSizeInflation);
- width = std::max(0, width);
- // update "height" (this is the ascent in the terminology of the REC)
- pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
- ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit;
- UpdateValue(mHeightSign, pseudoUnit, mHeight,
- aDesiredSize, height, fontSizeInflation);
- height = std::max(0, height);
- // update "depth" (this is the descent in the terminology of the REC)
- pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
- ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit;
- UpdateValue(mDepthSign, pseudoUnit, mDepth,
- aDesiredSize, depth, fontSizeInflation);
- depth = std::max(0, depth);
- // update lspace
- if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
- pseudoUnit = mLeadingSpacePseudoUnit;
- UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace,
- aDesiredSize, lspace, fontSizeInflation);
- }
- // update voffset
- if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
- pseudoUnit = mVerticalOffsetPseudoUnit;
- UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset,
- aDesiredSize, voffset, fontSizeInflation);
- }
- // do the padding now that we have everything
- // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
- // with no attributes) looks the same as <mrow>...</mrow>. But when there are
- // attributes, tweak our metrics and move children to achieve the desired visual
- // effects.
- if ((StyleVisibility()->mDirection ?
- mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) {
- // there was padding on the left. dismiss the left italic correction now
- // (so that our parent won't correct us)
- mBoundingMetrics.leftBearing = 0;
- }
- if ((StyleVisibility()->mDirection ?
- mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) {
- // there was padding on the right. dismiss the right italic correction now
- // (so that our parent won't correct us)
- mBoundingMetrics.width = width;
- mBoundingMetrics.rightBearing = mBoundingMetrics.width;
- }
- nscoord dx = (StyleVisibility()->mDirection ?
- width - initialWidth - lspace : lspace);
-
- aDesiredSize.SetBlockStartAscent(height);
- aDesiredSize.Width() = mBoundingMetrics.width;
- aDesiredSize.Height() = depth + aDesiredSize.BlockStartAscent();
- mBoundingMetrics.ascent = height;
- mBoundingMetrics.descent = depth;
- aDesiredSize.mBoundingMetrics = mBoundingMetrics;
- mReference.x = 0;
- mReference.y = aDesiredSize.BlockStartAscent();
- if (aPlaceOrigin) {
- // Finish reflowing child frames, positioning their origins.
- PositionRowChildFrames(dx, aDesiredSize.BlockStartAscent() - voffset);
- }
- return NS_OK;
- }
- /* virtual */ nsresult
- nsMathMLmpaddedFrame::MeasureForWidth(DrawTarget* aDrawTarget,
- ReflowOutput& aDesiredSize)
- {
- ProcessAttributes();
- return Place(aDrawTarget, false, aDesiredSize);
- }
|