layout/mathml/nsMathMLmoFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     6 #include "nsMathMLmoFrame.h"
     7 #include "nsPresContext.h"
     8 #include "nsRenderingContext.h"
     9 #include "nsContentUtils.h"
    10 #include "nsFrameSelection.h"
    11 #include "nsMathMLElement.h"
    12 #include <algorithm>
    14 //
    15 // <mo> -- operator, fence, or separator - implementation
    16 //
    18 // additional style context to be used by our MathMLChar.
    19 #define NS_MATHML_CHAR_STYLE_CONTEXT_INDEX   0
    21 nsIFrame*
    22 NS_NewMathMLmoFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
    23 {
    24   return new (aPresShell) nsMathMLmoFrame(aContext);
    25 }
    27 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)
    29 nsMathMLmoFrame::~nsMathMLmoFrame()
    30 {
    31 }
    33 static const char16_t kApplyFunction  = char16_t(0x2061);
    34 static const char16_t kInvisibleTimes = char16_t(0x2062);
    35 static const char16_t kInvisibleSeparator = char16_t(0x2063);
    36 static const char16_t kInvisiblePlus = char16_t(0x2064);
    38 eMathMLFrameType
    39 nsMathMLmoFrame::GetMathMLFrameType()
    40 {
    41   return NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)
    42     ? eMathMLFrameType_OperatorInvisible
    43     : eMathMLFrameType_OperatorOrdinary;
    44 }
    46 // since a mouse click implies selection, we cannot just rely on the
    47 // frame's state bit in our child text frame. So we will first check
    48 // its selected state bit, and use this little helper to double check.
    49 bool
    50 nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame)
    51 {
    52   NS_ASSERTION(aFrame, "null arg");
    53   if (!aFrame || !aFrame->IsSelected())
    54     return false;
    56   const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
    57   SelectionDetails* details =
    58     frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, true);
    60   if (!details)
    61     return false;
    63   while (details) {
    64     SelectionDetails* next = details->mNext;
    65     delete details;
    66     details = next;
    67   }
    68   return true;
    69 }
    71 bool
    72 nsMathMLmoFrame::UseMathMLChar()
    73 {
    74   return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
    75           NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
    76     NS_MATHML_OPERATOR_IS_CENTERED(mFlags);
    77 }
    79 void
    80 nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    81                                   const nsRect&           aDirtyRect,
    82                                   const nsDisplayListSet& aLists)
    83 {
    84   bool useMathMLChar = UseMathMLChar();
    86   if (!useMathMLChar) {
    87     // let the base class do everything
    88     nsMathMLTokenFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
    89   } else {
    90     DisplayBorderBackgroundOutline(aBuilder, aLists);
    92     // make our char selected if our inner child text frame is selected
    93     bool isSelected = false;
    94     nsRect selectedRect;
    95     nsIFrame* firstChild = mFrames.FirstChild();
    96     if (IsFrameInSelection(firstChild)) {
    97       mMathMLChar.GetRect(selectedRect);
    98       // add a one pixel border (it renders better for operators like minus)
    99       selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
   100       isSelected = true;
   101     }
   102     mMathMLChar.Display(aBuilder, this, aLists, 0, isSelected ? &selectedRect : nullptr);
   104 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
   105     // for visual debug
   106     DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
   107 #endif
   108   }
   109 }
   111 // get the text that we enclose and setup our nsMathMLChar
   112 void
   113 nsMathMLmoFrame::ProcessTextData()
   114 {
   115   mFlags = 0;
   117   nsAutoString data;
   118   if (!nsContentUtils::GetNodeTextContent(mContent, false, data)) {
   119     NS_RUNTIMEABORT("OOM");
   120   }
   122   data.CompressWhitespace();
   123   int32_t length = data.Length();
   124   char16_t ch = (length == 0) ? char16_t('\0') : data[0];
   126   if ((length == 1) && 
   127       (ch == kApplyFunction  ||
   128        ch == kInvisibleSeparator ||
   129        ch == kInvisiblePlus ||
   130        ch == kInvisibleTimes)) {
   131     mFlags |= NS_MATHML_OPERATOR_INVISIBLE;
   132   }
   134   // don't bother doing anything special if we don't have a single child
   135   nsPresContext* presContext = PresContext();
   136   if (mFrames.GetLength() != 1) {
   137     data.Truncate(); // empty data to reset the char
   138     mMathMLChar.SetData(presContext, data);
   139     ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
   140     return;
   141   }
   143   // special... in math mode, the usual minus sign '-' looks too short, so
   144   // what we do here is to remap <mo>-</mo> to the official Unicode minus
   145   // sign (U+2212) which looks much better. For background on this, see
   146   // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
   147   if (1 == length && ch == '-') {
   148     ch = 0x2212;
   149     data = ch;
   150   }
   152   // cache the special bits: mutable, accent, movablelimits, centered.
   153   // we need to do this in anticipation of other requirements, and these
   154   // bits don't change. Do not reset these bits unless the text gets changed.
   156   // lookup all the forms under which the operator is listed in the dictionary,
   157   // and record whether the operator has accent="true" or movablelimits="true"
   158   nsOperatorFlags flags[4];
   159   float lspace[4], rspace[4];
   160   nsMathMLOperators::LookupOperators(data, flags, lspace, rspace);
   161   nsOperatorFlags allFlags =
   162     flags[NS_MATHML_OPERATOR_FORM_INFIX] |
   163     flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
   164     flags[NS_MATHML_OPERATOR_FORM_PREFIX];
   166   mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT;
   167   mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS;
   169   // see if this is an operator that should be centered to cater for 
   170   // fonts that are not math-aware
   171   if (1 == length) {
   172     if ((ch == '+') || (ch == '=') || (ch == '*') ||
   173         (ch == 0x2212) || // &minus;
   174         (ch == 0x2264) || // &le;
   175         (ch == 0x2265) || // &ge;
   176         (ch == 0x00D7)) { // &times;
   177       mFlags |= NS_MATHML_OPERATOR_CENTERED;
   178     }
   179   }
   181   // cache the operator
   182   mMathMLChar.SetData(presContext, data);
   184   // cache the native direction -- beware of bug 133429...
   185   // mEmbellishData.direction must always retain our native direction, whereas
   186   // mMathMLChar.GetStretchDirection() may change later, when Stretch() is called
   187   mEmbellishData.direction = mMathMLChar.GetStretchDirection();
   189   bool isMutable =
   190     NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) ||
   191     (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED);
   192   if (isMutable)
   193     mFlags |= NS_MATHML_OPERATOR_MUTABLE;
   195   ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
   196 }
   198 // get our 'form' and lookup in the Operator Dictionary to fetch 
   199 // our default data that may come from there. Then complete our setup
   200 // using attributes that we may have. To stay in sync, this function is
   201 // called very often. We depend on many things that may change around us.
   202 // However, we re-use unchanged values.
   203 void
   204 nsMathMLmoFrame::ProcessOperatorData()
   205 {
   206   // if we have been here before, we will just use our cached form
   207   nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
   208   nsAutoString value;
   210   // special bits are always kept in mFlags.
   211   // remember the mutable bit from ProcessTextData().
   212   // Some chars are listed under different forms in the dictionary,
   213   // and there could be a form under which the char is mutable.
   214   // If the char is the core of an embellished container, we will keep
   215   // it mutable irrespective of the form of the embellished container.
   216   // Also remember the other special bits that we want to carry forward.
   217   mFlags &= NS_MATHML_OPERATOR_MUTABLE |
   218             NS_MATHML_OPERATOR_ACCENT | 
   219             NS_MATHML_OPERATOR_MOVABLELIMITS |
   220             NS_MATHML_OPERATOR_CENTERED |
   221             NS_MATHML_OPERATOR_INVISIBLE;
   223   if (!mEmbellishData.coreFrame) {
   224     // i.e., we haven't been here before, the default form is infix
   225     form = NS_MATHML_OPERATOR_FORM_INFIX;
   227     // reset everything so that we don't keep outdated values around
   228     // in case of dynamic changes
   229     mEmbellishData.flags = 0;
   230     mEmbellishData.coreFrame = nullptr;
   231     mEmbellishData.leadingSpace = 0;
   232     mEmbellishData.trailingSpace = 0;
   233     if (mMathMLChar.Length() != 1)
   234       mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;  
   235     // else... retain the native direction obtained in ProcessTextData()
   237     if (!mFrames.FirstChild()) {
   238       return;
   239     }
   241     mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
   242     mEmbellishData.coreFrame = this;
   244     // there are two particular things that we also need to record so that if our
   245     // parent is <mover>, <munder>, or <munderover>, they will treat us properly:
   246     // 1) do we have accent="true"
   247     // 2) do we have movablelimits="true"
   249     // they need the extra information to decide how to treat their scripts/limits
   250     // (note: <mover>, <munder>, or <munderover> need not necessarily be our
   251     // direct parent -- case of embellished operators)
   253     // default values from the Operator Dictionary were obtained in ProcessTextData()
   254     // and these special bits are always kept in mFlags
   255     if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags))
   256       mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
   257     if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags))
   258       mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
   260     // see if the accent attribute is there
   261     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value);
   262     if (value.EqualsLiteral("true"))
   263       mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
   264     else if (value.EqualsLiteral("false"))
   265       mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT;
   267     // see if the movablelimits attribute is there
   268     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::movablelimits_, value);
   269     if (value.EqualsLiteral("true"))
   270       mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
   271     else if (value.EqualsLiteral("false"))
   272       mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS;
   274      // ---------------------------------------------------------------------
   275      // we will be called again to re-sync the rest of our state next time...
   276      // (nobody needs the other values below at this stage)
   277      mFlags |= form;
   278      return;
   279   }
   281   nsPresContext* presContext = PresContext();
   283   // beware of bug 133814 - there is a two-way dependency in the
   284   // embellished hierarchy: our embellished ancestors need to set
   285   // their flags based on some of our state (set above), and here we
   286   // need to re-sync our 'form' depending on our outermost embellished
   287   // container. A null form here means that an earlier attempt to stretch
   288   // our mMathMLChar failed, in which case we don't bother re-stretching again
   289   if (form) {
   290     // get our outermost embellished container and its parent. 
   291     // (we ensure that we are the core, not just a sibling of the core)
   292     nsIFrame* embellishAncestor = this;
   293     nsEmbellishData embellishData;
   294     nsIFrame* parentAncestor = this;
   295     do {
   296       embellishAncestor = parentAncestor;
   297       parentAncestor = embellishAncestor->GetParent();
   298       GetEmbellishDataFrom(parentAncestor, embellishData);
   299     } while (embellishData.coreFrame == this);
   301     // flag if we have an embellished ancestor
   302     if (embellishAncestor != this)
   303       mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
   304     else
   305       mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
   307     // find the position of our outermost embellished container w.r.t
   308     // its siblings.
   310     nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
   311     nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
   313     // flag to distinguish from a real infix.  Set for (embellished) operators
   314     // that live in (inferred) mrows.
   315     nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
   316     bool zeroSpacing = false;
   317     if (mathAncestor) {
   318       zeroSpacing =  !mathAncestor->IsMrowLike();
   319     } else {
   320       nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
   321       if (blockFrame) {
   322         zeroSpacing = !blockFrame->IsMrowLike();
   323       }
   324     }
   325     if (zeroSpacing) {
   326       mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
   327     } else {
   328       mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
   329     }
   331     // find our form
   332     form = NS_MATHML_OPERATOR_FORM_INFIX;
   333     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value);
   334     if (!value.IsEmpty()) {
   335       if (value.EqualsLiteral("prefix"))
   336         form = NS_MATHML_OPERATOR_FORM_PREFIX;
   337       else if (value.EqualsLiteral("postfix"))
   338         form = NS_MATHML_OPERATOR_FORM_POSTFIX;
   339     }
   340     else {
   341       // set our form flag depending on the position
   342       if (!prevSibling && nextSibling)
   343         form = NS_MATHML_OPERATOR_FORM_PREFIX;
   344       else if (prevSibling && !nextSibling)
   345         form = NS_MATHML_OPERATOR_FORM_POSTFIX;
   346     }
   347     mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits
   348     mFlags |= form;
   350     // Use the default value suggested by the MathML REC.
   351     // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
   352     // thickmathspace = 5/18em
   353     float lspace = 5.0f/18.0f;
   354     float rspace = 5.0f/18.0f;
   355     // lookup the operator dictionary
   356     nsAutoString data;
   357     mMathMLChar.GetData(data);
   358     nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
   359     // Spacing is zero if our outermost embellished operator is not in an
   360     // inferred mrow.
   361     if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
   362         (lspace || rspace)) {
   363       // Cache the default values of lspace and rspace.
   364       // since these values are relative to the 'em' unit, convert to twips now
   365       nscoord em;
   366       nsRefPtr<nsFontMetrics> fm;
   367       nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   368       GetEmHeight(fm, em);
   370       mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
   371       mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
   373       // tuning if we don't want too much extra space when we are a script.
   374       // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
   375       // Our fonts can be anything, so...)
   376       if (StyleFont()->mScriptLevel > 0 &&
   377           !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
   378         mEmbellishData.leadingSpace /= 2;
   379         mEmbellishData.trailingSpace /= 2;
   380       }
   381     }
   382   }
   384   // If we are an accent without explicit lspace="." or rspace=".",
   385   // we will ignore our default leading/trailing space
   387   // lspace
   388   //
   389   // "Specifies the leading space appearing before the operator"
   390   //
   391   // values: length
   392   // default: set by dictionary (thickmathspace) 
   393   //
   394   // XXXfredw Support for negative and relative values is not implemented
   395   // (bug 805926).
   396   // Relative values will give a multiple of the current leading space,
   397   // which is not necessarily the default one.
   398   //
   399   nscoord leadingSpace = mEmbellishData.leadingSpace;
   400   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
   401   if (!value.IsEmpty()) {
   402     nsCSSValue cssValue;
   403     if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
   404                                            mContent->OwnerDoc())) {
   405       if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
   406         leadingSpace = 0;
   407       else if (cssValue.IsLengthUnit())
   408         leadingSpace = CalcLength(presContext, mStyleContext, cssValue);
   409       mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
   410     }
   411   }
   413   // rspace
   414   //
   415   // "Specifies the trailing space appearing after the operator"
   416   //
   417   // values: length
   418   // default: set by dictionary (thickmathspace) 
   419   //
   420   // XXXfredw Support for negative and relative values is not implemented
   421   // (bug 805926).
   422   // Relative values will give a multiple of the current leading space,
   423   // which is not necessarily the default one.
   424   //
   425   nscoord trailingSpace = mEmbellishData.trailingSpace;
   426   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rspace_, value);
   427   if (!value.IsEmpty()) {
   428     nsCSSValue cssValue;
   429     if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
   430                                            mContent->OwnerDoc())) {
   431       if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
   432         trailingSpace = 0;
   433       else if (cssValue.IsLengthUnit())
   434         trailingSpace = CalcLength(presContext, mStyleContext, cssValue);
   435       mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
   436     }
   437   }
   439   // little extra tuning to round lspace & rspace to at least a pixel so that
   440   // operators don't look as if they are colliding with their operands
   441   if (leadingSpace || trailingSpace) {
   442     nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
   443     if (leadingSpace && leadingSpace < onePixel)
   444       leadingSpace = onePixel;
   445     if (trailingSpace && trailingSpace < onePixel)
   446       trailingSpace = onePixel;
   447   }
   449   // the values that we get from our attributes override the dictionary
   450   mEmbellishData.leadingSpace = leadingSpace;
   451   mEmbellishData.trailingSpace = trailingSpace;
   453   // Now see if there are user-defined attributes that override the dictionary.
   454   // XXX If an attribute can be forced to be true when it is false in the
   455   // dictionary, then the following code has to change...
   457   // For each attribute overriden by the user, turn off its bit flag.
   458   // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
   459   // special: accent and movablelimits are handled above,
   460   // don't process them here
   462   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::stretchy_, value);
   463   if (value.EqualsLiteral("false")) {
   464     mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
   465   } else if (value.EqualsLiteral("true")) {
   466     mFlags |= NS_MATHML_OPERATOR_STRETCHY;
   467   }
   468   if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
   469     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::fence_, value);
   470     if (value.EqualsLiteral("false"))
   471       mFlags &= ~NS_MATHML_OPERATOR_FENCE;
   472   }
   473   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::largeop_, value);
   474   if (value.EqualsLiteral("false")) {
   475     mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
   476   } else if (value.EqualsLiteral("true")) {
   477     mFlags |= NS_MATHML_OPERATOR_LARGEOP;
   478   }
   479   if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
   480     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separator_, value);
   481     if (value.EqualsLiteral("false"))
   482       mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
   483   }
   484   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::symmetric_, value);
   485   if (value.EqualsLiteral("false"))
   486     mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
   487   else if (value.EqualsLiteral("true"))
   488     mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
   491   // minsize
   492   //
   493   // "Specifies the minimum size of the operator when stretchy"
   494   //
   495   // values: length
   496   // default: set by dictionary (1em)
   497   //
   498   // We don't allow negative values.
   499   // Note: Contrary to other "length" values, unitless and percentage do not
   500   // give a multiple of the defaut value but a multiple of the operator at
   501   // normal size.
   502   //
   503   mMinSize = 0;
   504   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::minsize_, value);
   505   if (!value.IsEmpty()) {
   506     nsCSSValue cssValue;
   507     if (nsMathMLElement::ParseNumericValue(value, cssValue,
   508                                            nsMathMLElement::
   509                                            PARSE_ALLOW_UNITLESS,
   510                                            mContent->OwnerDoc())) {
   511       nsCSSUnit unit = cssValue.GetUnit();
   512       if (eCSSUnit_Number == unit)
   513         mMinSize = cssValue.GetFloatValue();
   514       else if (eCSSUnit_Percent == unit)
   515         mMinSize = cssValue.GetPercentValue();
   516       else if (eCSSUnit_Null != unit) {
   517         mMinSize = float(CalcLength(presContext, mStyleContext, cssValue));
   518         mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
   519       }
   520     }
   521   }
   523   // maxsize
   524   //
   525   // "Specifies the maximum size of the operator when stretchy"
   526   //
   527   // values: length | "infinity"
   528   // default: set by dictionary (infinity)
   529   //
   530   // We don't allow negative values.
   531   // Note: Contrary to other "length" values, unitless and percentage do not
   532   // give a multiple of the defaut value but a multiple of the operator at
   533   // normal size.
   534   //
   535   mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
   536   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::maxsize_, value);
   537   if (!value.IsEmpty()) {
   538     nsCSSValue cssValue;
   539     if (nsMathMLElement::ParseNumericValue(value, cssValue,
   540                                            nsMathMLElement::
   541                                            PARSE_ALLOW_UNITLESS,
   542                                            mContent->OwnerDoc())) {
   543       nsCSSUnit unit = cssValue.GetUnit();
   544       if (eCSSUnit_Number == unit)
   545         mMaxSize = cssValue.GetFloatValue();
   546       else if (eCSSUnit_Percent == unit)
   547         mMaxSize = cssValue.GetPercentValue();
   548       else if (eCSSUnit_Null != unit) {
   549         mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue));
   550         mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
   551       }
   552     }
   553   }
   554 }
   556 static uint32_t
   557 GetStretchHint(nsOperatorFlags aFlags, nsPresentationData aPresentationData,
   558                bool aIsVertical, const nsStyleFont* aStyleFont)
   559 {
   560   uint32_t stretchHint = NS_STRETCH_NONE;
   561   // See if it is okay to stretch,
   562   // starting from what the Operator Dictionary said
   563   if (NS_MATHML_OPERATOR_IS_MUTABLE(aFlags)) {
   564     // set the largeop or largeopOnly flags to suitably cover all the
   565     // 8 possible cases depending on whether displaystyle, largeop,
   566     // stretchy are true or false (see bug 69325).
   567     // . largeopOnly is taken if largeop=true and stretchy=false
   568     // . largeop is taken if largeop=true and stretchy=true
   569     if (aStyleFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
   570         NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
   571       stretchHint = NS_STRETCH_LARGEOP; // (largeopOnly, not mask!)
   572       if (NS_MATHML_OPERATOR_IS_INTEGRAL(aFlags)) {
   573         stretchHint |= NS_STRETCH_INTEGRAL;
   574       }
   575       if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
   576         stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
   577       }
   578     }
   579     else if(NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
   580       if (aIsVertical) {
   581         // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
   582         stretchHint = NS_STRETCH_NEARER;
   583       }
   584       else {
   585         stretchHint = NS_STRETCH_NORMAL;
   586       }
   587     }
   588     // else if the stretchy and largeop attributes have been disabled,
   589     // the operator is not mutable
   590   }
   591   return stretchHint;
   592 }
   594 // NOTE: aDesiredStretchSize is an IN/OUT parameter
   595 //       On input  - it contains our current size
   596 //       On output - the same size or the new size that we want
   597 NS_IMETHODIMP
   598 nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext,
   599                          nsStretchDirection   aStretchDirection,
   600                          nsBoundingMetrics&   aContainerSize,
   601                          nsHTMLReflowMetrics& aDesiredStretchSize)
   602 {
   603   if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
   604     NS_WARNING("it is wrong to fire stretch more than once on a frame");
   605     return NS_OK;
   606   }
   607   mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
   609   nsIFrame* firstChild = mFrames.FirstChild();
   611   // get the axis height;
   612   nsRefPtr<nsFontMetrics> fm;
   613   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   614   aRenderingContext.SetFont(fm);
   615   nscoord axisHeight, height;
   616   GetAxisHeight(aRenderingContext, fm, axisHeight);
   618   // get the leading to be left at the top and the bottom of the stretched char
   619   // this seems more reliable than using fm->GetLeading() on suspicious fonts
   620   nscoord em;
   621   GetEmHeight(fm, em);
   622   nscoord leading = NSToCoordRound(0.2f * em);
   624   // Operators that are stretchy, or those that are to be centered
   625   // to cater for fonts that are not math-aware, are handled by the MathMLChar
   626   // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time)
   627   bool useMathMLChar = UseMathMLChar();
   629   nsBoundingMetrics charSize;
   630   nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
   631   bool isVertical = false;
   633   if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
   634        (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT))  &&
   635       (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
   636     isVertical = true;
   637   }
   639   uint32_t stretchHint =
   640     GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());
   642   if (useMathMLChar) {
   643     nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
   645     if (stretchHint != NS_STRETCH_NONE) {
   647       container = aContainerSize;
   649       // some adjustments if the operator is symmetric and vertical
   651       if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
   652         // we need to center about the axis
   653         nscoord delta = std::max(container.ascent - axisHeight,
   654                                container.descent + axisHeight);
   655         container.ascent = delta + axisHeight;
   656         container.descent = delta - axisHeight;
   658         // get ready in case we encounter user-desired min-max size
   659         delta = std::max(initialSize.ascent - axisHeight,
   660                        initialSize.descent + axisHeight);
   661         initialSize.ascent = delta + axisHeight;
   662         initialSize.descent = delta - axisHeight;
   663       }
   665       // check for user-desired min-max size
   667       if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
   668         // if we are here, there is a user defined maxsize ...
   669         //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible?
   670         if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
   671           // there is an explicit value like maxsize="20pt"
   672           // try to maintain the aspect ratio of the char
   673           float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent);
   674           container.ascent =
   675             std::min(container.ascent, nscoord(initialSize.ascent * aspect));
   676           container.descent =
   677             std::min(container.descent, nscoord(initialSize.descent * aspect));
   678           // below we use a type cast instead of a conversion to avoid a VC++ bug
   679           // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
   680           container.width =
   681             std::min(container.width, (nscoord)mMaxSize);
   682         }
   683         else { // multiplicative value
   684           container.ascent =
   685             std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize));
   686           container.descent =
   687             std::min(container.descent, nscoord(initialSize.descent * mMaxSize));
   688           container.width =
   689             std::min(container.width, nscoord(initialSize.width * mMaxSize));
   690         }
   692         if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
   693           // re-adjust to align the char with the bottom of the initial container
   694           height = container.ascent + container.descent;
   695           container.descent = aContainerSize.descent;
   696           container.ascent = height - container.descent;
   697         }
   698       }
   700       if (mMinSize > 0.0f) {
   701         // if we are here, there is a user defined minsize ...
   702         // always allow the char to stretch in its natural direction,
   703         // even if it is different from the caller's direction 
   704         if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
   705             aStretchDirection != mEmbellishData.direction) {
   706           aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
   707           // but when we are not honoring the requested direction
   708           // we should not use the caller's container size either
   709           container = initialSize;
   710         }
   711         if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
   712           // there is an explicit value like minsize="20pt"
   713           // try to maintain the aspect ratio of the char
   714           float aspect = mMinSize / float(initialSize.ascent + initialSize.descent);
   715           container.ascent =
   716             std::max(container.ascent, nscoord(initialSize.ascent * aspect));
   717           container.descent =
   718             std::max(container.descent, nscoord(initialSize.descent * aspect));
   719           container.width =
   720             std::max(container.width, (nscoord)mMinSize);
   721         }
   722         else { // multiplicative value
   723           container.ascent =
   724             std::max(container.ascent, nscoord(initialSize.ascent * mMinSize));
   725           container.descent =
   726             std::max(container.descent, nscoord(initialSize.descent * mMinSize));
   727           container.width =
   728             std::max(container.width, nscoord(initialSize.width * mMinSize));
   729         }
   731         if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
   732           // re-adjust to align the char with the bottom of the initial container
   733           height = container.ascent + container.descent;
   734           container.descent = aContainerSize.descent;
   735           container.ascent = height - container.descent;
   736         }
   737       }
   738     }
   740     // let the MathMLChar stretch itself...
   741     nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext,
   742                                        aStretchDirection, container, charSize,
   743                                        stretchHint,
   744                                        StyleVisibility()->mDirection);
   745     if (NS_FAILED(res)) {
   746       // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
   747       // clear our 'form' to behave as if the operator wasn't in the dictionary
   748       mFlags &= ~NS_MATHML_OPERATOR_FORM;
   749       useMathMLChar = false;
   750     }
   751   }
   753   // Place our children using the default method
   754   // This will allow our child text frame to get its DidReflow()
   755   nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
   756   if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   757     // Make sure the child frames get their DidReflow() calls.
   758     DidReflowChildren(mFrames.FirstChild());
   759   }
   761   if (useMathMLChar) {
   762     // update our bounding metrics... it becomes that of our MathML char
   763     mBoundingMetrics = charSize;
   765     // if the returned direction is 'unsupported', the char didn't actually change. 
   766     // So we do the centering only if necessary
   767     if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED ||
   768         NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
   770       bool largeopOnly =
   771         (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
   772         (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;
   774       if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
   775         // the desired size returned by mMathMLChar maybe different
   776         // from the size of the container.
   777         // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
   779         height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
   780         if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) ||
   781             NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
   782           // For symmetric and vertical operators, or for operators that are always
   783           // centered ('+', '*', etc) we want to center about the axis of the container
   784           mBoundingMetrics.descent = height/2 - axisHeight;
   785         } else if (!largeopOnly) {
   786           // Align the center of the char with the center of the container
   787           mBoundingMetrics.descent = height/2 +
   788             (container.ascent + container.descent)/2 - container.ascent;
   789         } // else align the baselines
   790         mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
   791       }
   792     }
   793   }
   795   // Fixup for the final height.
   796   // On one hand, our stretchy height can sometimes be shorter than surrounding
   797   // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading|
   798   // that is smaller than the ASCII's ascent, hence when painting the background
   799   // later, it won't look uniform along the line.
   800   // On the other hand, sometimes we may leave too much gap when our glyph happens
   801   // to come from a font with tall glyphs. For example, since CMEX10 has very tall
   802   // glyphs, its natural font metrics are large, even if we pick a small glyph
   803   // whose size is comparable to the size of a normal ASCII glyph.
   804   // So to avoid uneven spacing in either of these two cases, we use the height
   805   // of the ASCII font as a reference and try to match it if possible.
   807   // special case for accents... keep them short to improve mouse operations...
   808   // an accent can only be the non-first child of <mover>, <munder>, <munderover>
   809   bool isAccent =
   810     NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags);
   811   if (isAccent) {
   812     nsEmbellishData parentData;
   813     GetEmbellishDataFrom(mParent, parentData);
   814     isAccent =
   815        (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) ||
   816         NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) &&
   817        parentData.coreFrame != this;
   818   }
   819   if (isAccent && firstChild) {
   820     // see bug 188467 for what is going on here
   821     nscoord dy = aDesiredStretchSize.TopAscent() - (mBoundingMetrics.ascent + leading);
   822     aDesiredStretchSize.SetTopAscent(mBoundingMetrics.ascent + leading);
   823     aDesiredStretchSize.Height() = aDesiredStretchSize.TopAscent() + mBoundingMetrics.descent;
   825     firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
   826   }
   827   else if (useMathMLChar) {
   828     nscoord ascent = fm->MaxAscent();
   829     nscoord descent = fm->MaxDescent();
   830     aDesiredStretchSize.SetTopAscent(std::max(mBoundingMetrics.ascent + leading, ascent));
   831     aDesiredStretchSize.Height() = aDesiredStretchSize.TopAscent() +
   832                                  std::max(mBoundingMetrics.descent + leading, descent);
   833   }
   834   aDesiredStretchSize.Width() = mBoundingMetrics.width;
   835   aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
   836   mReference.x = 0;
   837   mReference.y = aDesiredStretchSize.TopAscent();
   838   // Place our mMathMLChar, its origin is in our coordinate system
   839   if (useMathMLChar) {
   840     nscoord dy = aDesiredStretchSize.TopAscent() - mBoundingMetrics.ascent;
   841     mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
   842   }
   844   // Before we leave... there is a last item in the check-list:
   845   // If our parent is not embellished, it means we are the outermost embellished
   846   // container and so we put the spacing, otherwise we don't include the spacing,
   847   // the outermost embellished container will take care of it.
   849   if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
   851     // Account the spacing if we are not an accent with explicit attributes
   852     nscoord leadingSpace = mEmbellishData.leadingSpace;
   853     if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
   854       leadingSpace = 0;
   855     }
   856     nscoord trailingSpace = mEmbellishData.trailingSpace;
   857     if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
   858       trailingSpace = 0;
   859     }
   861     mBoundingMetrics.width += leadingSpace + trailingSpace;
   862     aDesiredStretchSize.Width() = mBoundingMetrics.width;
   863     aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
   865     nscoord dx = (StyleVisibility()->mDirection ?
   866                   trailingSpace : leadingSpace);
   867     if (dx) {
   868       // adjust the offsets
   869       mBoundingMetrics.leftBearing += dx;
   870       mBoundingMetrics.rightBearing += dx;
   871       aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
   872       aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
   874       if (useMathMLChar) {
   875         nsRect rect;
   876         mMathMLChar.GetRect(rect);
   877         mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y,
   878                                    rect.width, rect.height));
   879       }
   880       else {
   881         nsIFrame* childFrame = firstChild;
   882         while (childFrame) {
   883           childFrame->SetPosition(childFrame->GetPosition() +
   884                                   nsPoint(dx, 0));
   885           childFrame = childFrame->GetNextSibling();
   886         }
   887       }
   888     }
   889   }
   891   // Finished with these:
   892   ClearSavedChildMetrics();
   893   // Set our overflow area
   894   GatherAndStoreOverflow(&aDesiredStretchSize);
   896   // There used to be code here to change the height of the child frame to
   897   // change the caret height, but the text frame that manages the caret is now
   898   // not a direct child but wrapped in a block frame.  See also bug 412033.
   900   return NS_OK;
   901 }
   903 NS_IMETHODIMP
   904 nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent)
   905 {
   906   // retain our native direction, it only changes if our text content changes
   907   nsStretchDirection direction = mEmbellishData.direction;
   908   nsMathMLTokenFrame::InheritAutomaticData(aParent);
   909   ProcessTextData();
   910   mEmbellishData.direction = direction;
   911   return NS_OK;
   912 }
   914 NS_IMETHODIMP
   915 nsMathMLmoFrame::TransmitAutomaticData()
   916 {
   917   // this will cause us to re-sync our flags from scratch
   918   // but our returned 'form' is still not final (bug 133429), it will
   919   // be recomputed to its final value during the next call in Reflow()
   920   mEmbellishData.coreFrame = nullptr;
   921   ProcessOperatorData();
   922   return NS_OK;
   923 }
   925 nsresult
   926 nsMathMLmoFrame::SetInitialChildList(ChildListID     aListID,
   927                                      nsFrameList&    aChildList)
   928 {
   929   // First, let the parent class do its work
   930   nsresult rv = nsMathMLTokenFrame::SetInitialChildList(aListID, aChildList);
   931   if (NS_FAILED(rv))
   932     return rv;
   934   ProcessTextData();
   935   return rv;
   936 }
   938 nsresult
   939 nsMathMLmoFrame::Reflow(nsPresContext*          aPresContext,
   940                         nsHTMLReflowMetrics&     aDesiredSize,
   941                         const nsHTMLReflowState& aReflowState,
   942                         nsReflowStatus&          aStatus)
   943 {
   944   // certain values use units that depend on our style context, so
   945   // it is safer to just process the whole lot here
   946   ProcessOperatorData();
   948   return nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize,
   949                                     aReflowState, aStatus);
   950 }
   952 /* virtual */ void
   953 nsMathMLmoFrame::MarkIntrinsicWidthsDirty()
   954 {
   955   // if we get this, it may mean that something changed in the text
   956   // content. So blow away everything an re-build the automatic data
   957   // from the parent of our outermost embellished container (we ensure
   958   // that we are the core, not just a sibling of the core)
   960   ProcessTextData();
   962   nsIFrame* target = this;
   963   nsEmbellishData embellishData;
   964   do {
   965     target = target->GetParent();
   966     GetEmbellishDataFrom(target, embellishData);
   967   } while (embellishData.coreFrame == this);
   969   // we have automatic data to update in the children of the target frame
   970   // XXXldb This should really be marking dirty rather than rebuilding
   971   // so that we don't rebuild multiple times for the same change.
   972   RebuildAutomaticDataForChildren(target);
   974   nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
   975 }
   977 /* virtual */ void
   978 nsMathMLmoFrame::GetIntrinsicWidthMetrics(nsRenderingContext *aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
   979 {
   980   ProcessOperatorData();
   981   if (UseMathMLChar()) {
   982     uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true,
   983                                           StyleFont());
   984     aDesiredSize.Width() = mMathMLChar.
   985       GetMaxWidth(PresContext(), *aRenderingContext,
   986                   stretchHint, mMaxSize,
   987                   NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags));
   988   }
   989   else {
   990     nsMathMLTokenFrame::GetIntrinsicWidthMetrics(aRenderingContext,
   991                                                  aDesiredSize);
   992   }
   994   // leadingSpace and trailingSpace are actually applied to the outermost
   995   // embellished container but for determining total intrinsic width it should
   996   // be safe to include it for the core here instead.
   997   bool isRTL = StyleVisibility()->mDirection;
   998   aDesiredSize.Width() +=
   999     mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
  1000   aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
  1001   if (isRTL) {
  1002     aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
  1003     aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
  1004   } else {
  1005     aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
  1006     aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
  1010 nsresult
  1011 nsMathMLmoFrame::AttributeChanged(int32_t         aNameSpaceID,
  1012                                   nsIAtom*        aAttribute,
  1013                                   int32_t         aModType)
  1015   // check if this is an attribute that can affect the embellished hierarchy
  1016   // in a significant way and re-layout the entire hierarchy.
  1017   if (nsGkAtoms::accent_ == aAttribute ||
  1018       nsGkAtoms::movablelimits_ == aAttribute) {
  1020     // set the target as the parent of our outermost embellished container
  1021     // (we ensure that we are the core, not just a sibling of the core)
  1022     nsIFrame* target = this;
  1023     nsEmbellishData embellishData;
  1024     do {
  1025       target = target->GetParent();
  1026       GetEmbellishDataFrom(target, embellishData);
  1027     } while (embellishData.coreFrame == this);
  1029     // we have automatic data to update in the children of the target frame
  1030     return ReLayoutChildren(target);
  1033   return nsMathMLTokenFrame::
  1034          AttributeChanged(aNameSpaceID, aAttribute, aModType);
  1037 // ----------------------
  1038 // No need to track the style context given to our MathML char. 
  1039 // the Style System will use these to pass the proper style context to our MathMLChar
  1040 nsStyleContext*
  1041 nsMathMLmoFrame::GetAdditionalStyleContext(int32_t aIndex) const
  1043   switch (aIndex) {
  1044   case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
  1045     return mMathMLChar.GetStyleContext();
  1046   default:
  1047     return nullptr;
  1051 void
  1052 nsMathMLmoFrame::SetAdditionalStyleContext(int32_t          aIndex,
  1053                                            nsStyleContext*  aStyleContext)
  1055   switch (aIndex) {
  1056   case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
  1057     mMathMLChar.SetStyleContext(aStyleContext);
  1058     break;

mercurial