LCOV - code coverage report
Current view: top level - layout/mathml - nsMathMLmpaddedFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 195 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : 
       7             : #include "nsMathMLmpaddedFrame.h"
       8             : #include "nsMathMLElement.h"
       9             : #include "mozilla/gfx/2D.h"
      10             : #include <algorithm>
      11             : 
      12             : //
      13             : // <mpadded> -- adjust space around content - implementation
      14             : //
      15             : 
      16             : #define NS_MATHML_SIGN_INVALID           -1 // if the attribute is not there
      17             : #define NS_MATHML_SIGN_UNSPECIFIED        0
      18             : #define NS_MATHML_SIGN_MINUS              1
      19             : #define NS_MATHML_SIGN_PLUS               2
      20             : 
      21             : #define NS_MATHML_PSEUDO_UNIT_UNSPECIFIED 0
      22             : #define NS_MATHML_PSEUDO_UNIT_ITSELF      1 // special
      23             : #define NS_MATHML_PSEUDO_UNIT_WIDTH       2
      24             : #define NS_MATHML_PSEUDO_UNIT_HEIGHT      3
      25             : #define NS_MATHML_PSEUDO_UNIT_DEPTH       4
      26             : #define NS_MATHML_PSEUDO_UNIT_NAMEDSPACE  5
      27             : 
      28             : nsIFrame*
      29           0 : NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      30             : {
      31           0 :   return new (aPresShell) nsMathMLmpaddedFrame(aContext);
      32             : }
      33             : 
      34           0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmpaddedFrame)
      35             : 
      36           0 : nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame()
      37             : {
      38           0 : }
      39             : 
      40             : NS_IMETHODIMP
      41           0 : nsMathMLmpaddedFrame::InheritAutomaticData(nsIFrame* aParent)
      42             : {
      43             :   // let the base class get the default from our parent
      44           0 :   nsMathMLContainerFrame::InheritAutomaticData(aParent);
      45             : 
      46           0 :   mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
      47             : 
      48           0 :   return NS_OK;
      49             : }
      50             : 
      51             : void
      52           0 : nsMathMLmpaddedFrame::ProcessAttributes()
      53             : {
      54             :   /*
      55             :   parse the attributes
      56             : 
      57             :   width  = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
      58             :   height = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
      59             :   depth  = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
      60             :   lspace = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
      61             :   voffset= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
      62             :   */
      63             : 
      64           0 :   nsAutoString value;
      65             : 
      66             :   // width
      67           0 :   mWidthSign = NS_MATHML_SIGN_INVALID;
      68           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value);
      69           0 :   if (!value.IsEmpty()) {
      70           0 :     if (!ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit)) {
      71           0 :       ReportParseError(nsGkAtoms::width->GetUTF16String(), value.get());
      72             :     }
      73             :   }
      74             : 
      75             :   // height
      76           0 :   mHeightSign = NS_MATHML_SIGN_INVALID;
      77           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value);
      78           0 :   if (!value.IsEmpty()) {
      79           0 :     if (!ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit)) {
      80           0 :       ReportParseError(nsGkAtoms::height->GetUTF16String(), value.get());
      81             :     }
      82             :   }
      83             : 
      84             :   // depth
      85           0 :   mDepthSign = NS_MATHML_SIGN_INVALID;
      86           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::depth_, value);
      87           0 :   if (!value.IsEmpty()) {
      88           0 :     if (!ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit)) {
      89           0 :       ReportParseError(nsGkAtoms::depth_->GetUTF16String(), value.get());
      90             :     }
      91             :   }
      92             : 
      93             :   // lspace
      94           0 :   mLeadingSpaceSign = NS_MATHML_SIGN_INVALID;
      95           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
      96           0 :   if (!value.IsEmpty()) {
      97           0 :     if (!ParseAttribute(value, mLeadingSpaceSign, mLeadingSpace,
      98             :                         mLeadingSpacePseudoUnit)) {
      99           0 :       ReportParseError(nsGkAtoms::lspace_->GetUTF16String(), value.get());
     100             :     }
     101             :   }
     102             : 
     103             :   // voffset
     104           0 :   mVerticalOffsetSign = NS_MATHML_SIGN_INVALID;
     105           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::voffset_, value);
     106           0 :   if (!value.IsEmpty()) {
     107           0 :     if (!ParseAttribute(value, mVerticalOffsetSign, mVerticalOffset,
     108             :                         mVerticalOffsetPseudoUnit)) {
     109           0 :       ReportParseError(nsGkAtoms::voffset_->GetUTF16String(), value.get());
     110             :     }
     111             :   }
     112             : 
     113           0 : }
     114             : 
     115             : // parse an input string in the following format (see bug 148326 for testcases):
     116             : // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
     117             : bool
     118           0 : nsMathMLmpaddedFrame::ParseAttribute(nsString&   aString,
     119             :                                      int32_t&    aSign,
     120             :                                      nsCSSValue& aCSSValue,
     121             :                                      int32_t&    aPseudoUnit)
     122             : {
     123           0 :   aCSSValue.Reset();
     124           0 :   aSign = NS_MATHML_SIGN_INVALID;
     125           0 :   aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED;
     126           0 :   aString.CompressWhitespace(); // aString is not a const in this code
     127             : 
     128           0 :   int32_t stringLength = aString.Length();
     129           0 :   if (!stringLength)
     130           0 :     return false;
     131             : 
     132           0 :   nsAutoString number, unit;
     133             : 
     134             :   //////////////////////
     135             :   // see if the sign is there
     136             : 
     137           0 :   int32_t i = 0;
     138             : 
     139           0 :   if (aString[0] == '+') {
     140           0 :     aSign = NS_MATHML_SIGN_PLUS;
     141           0 :     i++;
     142             :   }
     143           0 :   else if (aString[0] == '-') {
     144           0 :     aSign = NS_MATHML_SIGN_MINUS;
     145           0 :     i++;
     146             :   }
     147             :   else
     148           0 :     aSign = NS_MATHML_SIGN_UNSPECIFIED;
     149             : 
     150             :   // get the number
     151           0 :   bool gotDot = false, gotPercent = false;
     152           0 :   for (; i < stringLength; i++) {
     153           0 :     char16_t c = aString[i];
     154           0 :     if (gotDot && c == '.') {
     155             :       // error - two dots encountered
     156           0 :       aSign = NS_MATHML_SIGN_INVALID;
     157           0 :       return false;
     158             :     }
     159             : 
     160           0 :     if (c == '.')
     161           0 :       gotDot = true;
     162           0 :     else if (!nsCRT::IsAsciiDigit(c)) {
     163           0 :       break;
     164             :     }
     165           0 :     number.Append(c);
     166             :   }
     167             : 
     168             :   // catch error if we didn't enter the loop above... we could simply initialize
     169             :   // floatValue = 1, to cater for cases such as width="height", but that wouldn't
     170             :   // be in line with the spec which requires an explicit number
     171           0 :   if (number.IsEmpty()) {
     172           0 :     aSign = NS_MATHML_SIGN_INVALID;
     173           0 :     return false;
     174             :   }
     175             : 
     176             :   nsresult errorCode;
     177           0 :   float floatValue = number.ToFloat(&errorCode);
     178           0 :   if (NS_FAILED(errorCode)) {
     179           0 :     aSign = NS_MATHML_SIGN_INVALID;
     180           0 :     return false;
     181             :   }
     182             : 
     183             :   // see if this is a percentage-based value
     184           0 :   if (i < stringLength && aString[i] == '%') {
     185           0 :     i++;
     186           0 :     gotPercent = true;
     187             :   }
     188             : 
     189             :   // the remainder now should be a css-unit, or a pseudo-unit, or a named-space
     190           0 :   aString.Right(unit, stringLength - i);
     191             : 
     192           0 :   if (unit.IsEmpty()) {
     193           0 :     if (gotPercent) {
     194             :       // case ["+"|"-"] unsigned-number "%"
     195           0 :       aCSSValue.SetPercentValue(floatValue / 100.0f);
     196           0 :       aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
     197           0 :       return true;
     198             :     } else {
     199             :       // case ["+"|"-"] unsigned-number
     200             :       // XXXfredw: should we allow non-zero unitless values? See bug 757703.
     201           0 :       if (!floatValue) {
     202           0 :         aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
     203           0 :         aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
     204           0 :         return true;
     205             :       }
     206             :     }
     207             :   }
     208           0 :   else if (unit.EqualsLiteral("width"))  aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH;
     209           0 :   else if (unit.EqualsLiteral("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT;
     210           0 :   else if (unit.EqualsLiteral("depth"))  aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH;
     211           0 :   else if (!gotPercent) { // percentage can only apply to a pseudo-unit
     212             : 
     213             :     // see if the unit is a named-space
     214           0 :     if (nsMathMLElement::ParseNamedSpaceValue(unit, aCSSValue,
     215             :                                               nsMathMLElement::
     216             :                                               PARSE_ALLOW_NEGATIVE)) {
     217             :       // re-scale properly, and we know that the unit of the named-space is 'em'
     218           0 :       floatValue *= aCSSValue.GetFloatValue();
     219           0 :       aCSSValue.SetFloatValue(floatValue, eCSSUnit_EM);
     220           0 :       aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE;
     221           0 :       return true;
     222             :     }
     223             : 
     224             :     // see if the input was just a CSS value
     225             :     // We are not supposed to have a unitless, percent, negative or namedspace
     226             :     // value here.
     227           0 :     number.Append(unit); // leave the sign out if it was there
     228           0 :     if (nsMathMLElement::ParseNumericValue(number, aCSSValue,
     229             :                                            nsMathMLElement::
     230             :                                            PARSE_SUPPRESS_WARNINGS, nullptr))
     231           0 :       return true;
     232             :   }
     233             : 
     234             :   // if we enter here, we have a number that will act as a multiplier on a pseudo-unit
     235           0 :   if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) {
     236           0 :     if (gotPercent)
     237           0 :       aCSSValue.SetPercentValue(floatValue / 100.0f);
     238             :     else
     239           0 :       aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
     240             : 
     241           0 :     return true;
     242             :   }
     243             : 
     244             : 
     245             : #ifdef DEBUG
     246           0 :   printf("mpadded: attribute with bad numeric value: %s\n",
     247           0 :           NS_LossyConvertUTF16toASCII(aString).get());
     248             : #endif
     249             :   // if we reach here, it means we encounter an unexpected input
     250           0 :   aSign = NS_MATHML_SIGN_INVALID;
     251           0 :   return false;
     252             : }
     253             : 
     254             : void
     255           0 : nsMathMLmpaddedFrame::UpdateValue(int32_t                  aSign,
     256             :                                   int32_t                  aPseudoUnit,
     257             :                                   const nsCSSValue&        aCSSValue,
     258             :                                   const ReflowOutput& aDesiredSize,
     259             :                                   nscoord&                 aValueToUpdate,
     260             :                                   float                aFontSizeInflation) const
     261             : {
     262           0 :   nsCSSUnit unit = aCSSValue.GetUnit();
     263           0 :   if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) {
     264           0 :     nscoord scaler = 0, amount = 0;
     265             : 
     266           0 :     if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) {
     267           0 :       switch(aPseudoUnit) {
     268             :         case NS_MATHML_PSEUDO_UNIT_WIDTH:
     269           0 :              scaler = aDesiredSize.Width();
     270           0 :              break;
     271             : 
     272             :         case NS_MATHML_PSEUDO_UNIT_HEIGHT:
     273           0 :              scaler = aDesiredSize.BlockStartAscent();
     274           0 :              break;
     275             : 
     276             :         case NS_MATHML_PSEUDO_UNIT_DEPTH:
     277           0 :              scaler = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
     278           0 :              break;
     279             : 
     280             :         default:
     281             :           // if we ever reach here, it would mean something is wrong
     282             :           // somewhere with the setup and/or the caller
     283           0 :           NS_ERROR("Unexpected Pseudo Unit");
     284           0 :           return;
     285             :       }
     286             :     }
     287             : 
     288           0 :     if (eCSSUnit_Number == unit)
     289           0 :       amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue());
     290           0 :     else if (eCSSUnit_Percent == unit)
     291           0 :       amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue());
     292             :     else
     293           0 :       amount = CalcLength(PresContext(), mStyleContext, aCSSValue,
     294           0 :                           aFontSizeInflation);
     295             : 
     296           0 :     if (NS_MATHML_SIGN_PLUS == aSign)
     297           0 :       aValueToUpdate += amount;
     298           0 :     else if (NS_MATHML_SIGN_MINUS == aSign)
     299           0 :       aValueToUpdate -= amount;
     300             :     else
     301           0 :       aValueToUpdate  = amount;
     302             :   }
     303             : }
     304             : 
     305             : void
     306           0 : nsMathMLmpaddedFrame::Reflow(nsPresContext*          aPresContext,
     307             :                              ReflowOutput&     aDesiredSize,
     308             :                              const ReflowInput& aReflowInput,
     309             :                              nsReflowStatus&          aStatus)
     310             : {
     311           0 :   mPresentationData.flags &= ~NS_MATHML_ERROR;
     312           0 :   ProcessAttributes();
     313             : 
     314             :   ///////////////
     315             :   // Let the base class format our content like an inferred mrow
     316           0 :   nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
     317           0 :                                  aReflowInput, aStatus);
     318             :   //NS_ASSERTION(aStatus.IsComplete(), "bad status");
     319           0 : }
     320             : 
     321             : /* virtual */ nsresult
     322           0 : nsMathMLmpaddedFrame::Place(DrawTarget*          aDrawTarget,
     323             :                             bool                 aPlaceOrigin,
     324             :                             ReflowOutput& aDesiredSize)
     325             : {
     326             :   nsresult rv =
     327           0 :     nsMathMLContainerFrame::Place(aDrawTarget, false, aDesiredSize);
     328           0 :   if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
     329           0 :     DidReflowChildren(PrincipalChildList().FirstChild());
     330           0 :     return rv;
     331             :   }
     332             : 
     333           0 :   nscoord height = aDesiredSize.BlockStartAscent();
     334           0 :   nscoord depth  = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
     335             :   // The REC says:
     336             :   //
     337             :   // "The lspace attribute ('leading' space) specifies the horizontal location
     338             :   // of the positioning point of the child content with respect to the
     339             :   // positioning point of the mpadded element. By default they coincide, and
     340             :   // therefore absolute values for lspace have the same effect as relative
     341             :   // values."
     342             :   //
     343             :   // "MathML renderers should ensure that, except for the effects of the
     344             :   // attributes, the relative spacing between the contents of the mpadded
     345             :   // element and surrounding MathML elements would not be modified by replacing
     346             :   // an mpadded element with an mrow element with the same content, even if
     347             :   // linebreaking occurs within the mpadded element."
     348             :   //
     349             :   // (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded)
     350             :   //
     351             :   // "In those discussions, the terms leading and trailing are used to specify
     352             :   // a side of an object when which side to use depends on the directionality;
     353             :   // ie. leading means left in LTR but right in RTL."
     354             :   // (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math)
     355           0 :   nscoord lspace = 0;
     356             :   // In MathML3, "width" will be the bounding box width and "advancewidth" will
     357             :   // refer "to the horizontal distance between the positioning point of the
     358             :   // mpadded and the positioning point for the following content".  MathML2
     359             :   // doesn't make the distinction.
     360           0 :   nscoord width  = aDesiredSize.Width();
     361           0 :   nscoord voffset = 0;
     362             : 
     363             :   int32_t pseudoUnit;
     364           0 :   nscoord initialWidth = width;
     365           0 :   float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
     366             : 
     367             :   // update width
     368           0 :   pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
     369           0 :              ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit;
     370           0 :   UpdateValue(mWidthSign, pseudoUnit, mWidth,
     371           0 :               aDesiredSize, width, fontSizeInflation);
     372           0 :   width = std::max(0, width);
     373             : 
     374             :   // update "height" (this is the ascent in the terminology of the REC)
     375           0 :   pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
     376           0 :              ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit;
     377           0 :   UpdateValue(mHeightSign, pseudoUnit, mHeight,
     378           0 :               aDesiredSize, height, fontSizeInflation);
     379           0 :   height = std::max(0, height);
     380             : 
     381             :   // update "depth" (this is the descent in the terminology of the REC)
     382           0 :   pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
     383           0 :              ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit;
     384           0 :   UpdateValue(mDepthSign, pseudoUnit, mDepth,
     385           0 :               aDesiredSize, depth, fontSizeInflation);
     386           0 :   depth = std::max(0, depth);
     387             : 
     388             :   // update lspace
     389           0 :   if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
     390           0 :     pseudoUnit = mLeadingSpacePseudoUnit;
     391           0 :     UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace,
     392           0 :                 aDesiredSize, lspace, fontSizeInflation);
     393             :   }
     394             : 
     395             :   // update voffset
     396           0 :   if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
     397           0 :     pseudoUnit = mVerticalOffsetPseudoUnit;
     398           0 :     UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset,
     399           0 :                 aDesiredSize, voffset, fontSizeInflation);
     400             :   }
     401             :   // do the padding now that we have everything
     402             :   // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
     403             :   // with no attributes) looks the same as <mrow>...</mrow>. But when there are
     404             :   // attributes, tweak our metrics and move children to achieve the desired visual
     405             :   // effects.
     406             : 
     407           0 :   if ((StyleVisibility()->mDirection ?
     408           0 :        mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) {
     409             :     // there was padding on the left. dismiss the left italic correction now
     410             :     // (so that our parent won't correct us)
     411           0 :     mBoundingMetrics.leftBearing = 0;
     412             :   }
     413             : 
     414           0 :   if ((StyleVisibility()->mDirection ?
     415           0 :        mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) {
     416             :     // there was padding on the right. dismiss the right italic correction now
     417             :     // (so that our parent won't correct us)
     418           0 :     mBoundingMetrics.width = width;
     419           0 :     mBoundingMetrics.rightBearing = mBoundingMetrics.width;
     420             :   }
     421             : 
     422           0 :   nscoord dx = (StyleVisibility()->mDirection ?
     423           0 :                 width - initialWidth - lspace : lspace);
     424             : 
     425           0 :   aDesiredSize.SetBlockStartAscent(height);
     426           0 :   aDesiredSize.Width() = mBoundingMetrics.width;
     427           0 :   aDesiredSize.Height() = depth + aDesiredSize.BlockStartAscent();
     428           0 :   mBoundingMetrics.ascent = height;
     429           0 :   mBoundingMetrics.descent = depth;
     430           0 :   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
     431             : 
     432           0 :   mReference.x = 0;
     433           0 :   mReference.y = aDesiredSize.BlockStartAscent();
     434             : 
     435           0 :   if (aPlaceOrigin) {
     436             :     // Finish reflowing child frames, positioning their origins.
     437           0 :     PositionRowChildFrames(dx, aDesiredSize.BlockStartAscent() - voffset);
     438             :   }
     439             : 
     440           0 :   return NS_OK;
     441             : }
     442             : 
     443             : /* virtual */ nsresult
     444           0 : nsMathMLmpaddedFrame::MeasureForWidth(DrawTarget* aDrawTarget,
     445             :                                       ReflowOutput& aDesiredSize)
     446             : {
     447           0 :   ProcessAttributes();
     448           0 :   return Place(aDrawTarget, false, aDesiredSize);
     449             : }

Generated by: LCOV version 1.13