layout/mathml/nsMathMLmoFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/mathml/nsMathMLmoFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1060 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsMathMLmoFrame.h"
    1.10 +#include "nsPresContext.h"
    1.11 +#include "nsRenderingContext.h"
    1.12 +#include "nsContentUtils.h"
    1.13 +#include "nsFrameSelection.h"
    1.14 +#include "nsMathMLElement.h"
    1.15 +#include <algorithm>
    1.16 +
    1.17 +//
    1.18 +// <mo> -- operator, fence, or separator - implementation
    1.19 +//
    1.20 +
    1.21 +// additional style context to be used by our MathMLChar.
    1.22 +#define NS_MATHML_CHAR_STYLE_CONTEXT_INDEX   0
    1.23 +
    1.24 +nsIFrame*
    1.25 +NS_NewMathMLmoFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
    1.26 +{
    1.27 +  return new (aPresShell) nsMathMLmoFrame(aContext);
    1.28 +}
    1.29 +
    1.30 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)
    1.31 +
    1.32 +nsMathMLmoFrame::~nsMathMLmoFrame()
    1.33 +{
    1.34 +}
    1.35 +
    1.36 +static const char16_t kApplyFunction  = char16_t(0x2061);
    1.37 +static const char16_t kInvisibleTimes = char16_t(0x2062);
    1.38 +static const char16_t kInvisibleSeparator = char16_t(0x2063);
    1.39 +static const char16_t kInvisiblePlus = char16_t(0x2064);
    1.40 +
    1.41 +eMathMLFrameType
    1.42 +nsMathMLmoFrame::GetMathMLFrameType()
    1.43 +{
    1.44 +  return NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)
    1.45 +    ? eMathMLFrameType_OperatorInvisible
    1.46 +    : eMathMLFrameType_OperatorOrdinary;
    1.47 +}
    1.48 +
    1.49 +// since a mouse click implies selection, we cannot just rely on the
    1.50 +// frame's state bit in our child text frame. So we will first check
    1.51 +// its selected state bit, and use this little helper to double check.
    1.52 +bool
    1.53 +nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame)
    1.54 +{
    1.55 +  NS_ASSERTION(aFrame, "null arg");
    1.56 +  if (!aFrame || !aFrame->IsSelected())
    1.57 +    return false;
    1.58 +
    1.59 +  const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
    1.60 +  SelectionDetails* details =
    1.61 +    frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, true);
    1.62 +
    1.63 +  if (!details)
    1.64 +    return false;
    1.65 +
    1.66 +  while (details) {
    1.67 +    SelectionDetails* next = details->mNext;
    1.68 +    delete details;
    1.69 +    details = next;
    1.70 +  }
    1.71 +  return true;
    1.72 +}
    1.73 +
    1.74 +bool
    1.75 +nsMathMLmoFrame::UseMathMLChar()
    1.76 +{
    1.77 +  return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
    1.78 +          NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
    1.79 +    NS_MATHML_OPERATOR_IS_CENTERED(mFlags);
    1.80 +}
    1.81 +
    1.82 +void
    1.83 +nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    1.84 +                                  const nsRect&           aDirtyRect,
    1.85 +                                  const nsDisplayListSet& aLists)
    1.86 +{
    1.87 +  bool useMathMLChar = UseMathMLChar();
    1.88 +
    1.89 +  if (!useMathMLChar) {
    1.90 +    // let the base class do everything
    1.91 +    nsMathMLTokenFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
    1.92 +  } else {
    1.93 +    DisplayBorderBackgroundOutline(aBuilder, aLists);
    1.94 +    
    1.95 +    // make our char selected if our inner child text frame is selected
    1.96 +    bool isSelected = false;
    1.97 +    nsRect selectedRect;
    1.98 +    nsIFrame* firstChild = mFrames.FirstChild();
    1.99 +    if (IsFrameInSelection(firstChild)) {
   1.100 +      mMathMLChar.GetRect(selectedRect);
   1.101 +      // add a one pixel border (it renders better for operators like minus)
   1.102 +      selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
   1.103 +      isSelected = true;
   1.104 +    }
   1.105 +    mMathMLChar.Display(aBuilder, this, aLists, 0, isSelected ? &selectedRect : nullptr);
   1.106 +  
   1.107 +#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
   1.108 +    // for visual debug
   1.109 +    DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
   1.110 +#endif
   1.111 +  }
   1.112 +}
   1.113 +
   1.114 +// get the text that we enclose and setup our nsMathMLChar
   1.115 +void
   1.116 +nsMathMLmoFrame::ProcessTextData()
   1.117 +{
   1.118 +  mFlags = 0;
   1.119 +
   1.120 +  nsAutoString data;
   1.121 +  if (!nsContentUtils::GetNodeTextContent(mContent, false, data)) {
   1.122 +    NS_RUNTIMEABORT("OOM");
   1.123 +  }
   1.124 +
   1.125 +  data.CompressWhitespace();
   1.126 +  int32_t length = data.Length();
   1.127 +  char16_t ch = (length == 0) ? char16_t('\0') : data[0];
   1.128 +
   1.129 +  if ((length == 1) && 
   1.130 +      (ch == kApplyFunction  ||
   1.131 +       ch == kInvisibleSeparator ||
   1.132 +       ch == kInvisiblePlus ||
   1.133 +       ch == kInvisibleTimes)) {
   1.134 +    mFlags |= NS_MATHML_OPERATOR_INVISIBLE;
   1.135 +  }
   1.136 +
   1.137 +  // don't bother doing anything special if we don't have a single child
   1.138 +  nsPresContext* presContext = PresContext();
   1.139 +  if (mFrames.GetLength() != 1) {
   1.140 +    data.Truncate(); // empty data to reset the char
   1.141 +    mMathMLChar.SetData(presContext, data);
   1.142 +    ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
   1.143 +    return;
   1.144 +  }
   1.145 +
   1.146 +  // special... in math mode, the usual minus sign '-' looks too short, so
   1.147 +  // what we do here is to remap <mo>-</mo> to the official Unicode minus
   1.148 +  // sign (U+2212) which looks much better. For background on this, see
   1.149 +  // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
   1.150 +  if (1 == length && ch == '-') {
   1.151 +    ch = 0x2212;
   1.152 +    data = ch;
   1.153 +  }
   1.154 +
   1.155 +  // cache the special bits: mutable, accent, movablelimits, centered.
   1.156 +  // we need to do this in anticipation of other requirements, and these
   1.157 +  // bits don't change. Do not reset these bits unless the text gets changed.
   1.158 +
   1.159 +  // lookup all the forms under which the operator is listed in the dictionary,
   1.160 +  // and record whether the operator has accent="true" or movablelimits="true"
   1.161 +  nsOperatorFlags flags[4];
   1.162 +  float lspace[4], rspace[4];
   1.163 +  nsMathMLOperators::LookupOperators(data, flags, lspace, rspace);
   1.164 +  nsOperatorFlags allFlags =
   1.165 +    flags[NS_MATHML_OPERATOR_FORM_INFIX] |
   1.166 +    flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
   1.167 +    flags[NS_MATHML_OPERATOR_FORM_PREFIX];
   1.168 +
   1.169 +  mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT;
   1.170 +  mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS;
   1.171 +
   1.172 +  // see if this is an operator that should be centered to cater for 
   1.173 +  // fonts that are not math-aware
   1.174 +  if (1 == length) {
   1.175 +    if ((ch == '+') || (ch == '=') || (ch == '*') ||
   1.176 +        (ch == 0x2212) || // &minus;
   1.177 +        (ch == 0x2264) || // &le;
   1.178 +        (ch == 0x2265) || // &ge;
   1.179 +        (ch == 0x00D7)) { // &times;
   1.180 +      mFlags |= NS_MATHML_OPERATOR_CENTERED;
   1.181 +    }
   1.182 +  }
   1.183 +
   1.184 +  // cache the operator
   1.185 +  mMathMLChar.SetData(presContext, data);
   1.186 +
   1.187 +  // cache the native direction -- beware of bug 133429...
   1.188 +  // mEmbellishData.direction must always retain our native direction, whereas
   1.189 +  // mMathMLChar.GetStretchDirection() may change later, when Stretch() is called
   1.190 +  mEmbellishData.direction = mMathMLChar.GetStretchDirection();
   1.191 +
   1.192 +  bool isMutable =
   1.193 +    NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) ||
   1.194 +    (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED);
   1.195 +  if (isMutable)
   1.196 +    mFlags |= NS_MATHML_OPERATOR_MUTABLE;
   1.197 +
   1.198 +  ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
   1.199 +}
   1.200 +
   1.201 +// get our 'form' and lookup in the Operator Dictionary to fetch 
   1.202 +// our default data that may come from there. Then complete our setup
   1.203 +// using attributes that we may have. To stay in sync, this function is
   1.204 +// called very often. We depend on many things that may change around us.
   1.205 +// However, we re-use unchanged values.
   1.206 +void
   1.207 +nsMathMLmoFrame::ProcessOperatorData()
   1.208 +{
   1.209 +  // if we have been here before, we will just use our cached form
   1.210 +  nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
   1.211 +  nsAutoString value;
   1.212 +
   1.213 +  // special bits are always kept in mFlags.
   1.214 +  // remember the mutable bit from ProcessTextData().
   1.215 +  // Some chars are listed under different forms in the dictionary,
   1.216 +  // and there could be a form under which the char is mutable.
   1.217 +  // If the char is the core of an embellished container, we will keep
   1.218 +  // it mutable irrespective of the form of the embellished container.
   1.219 +  // Also remember the other special bits that we want to carry forward.
   1.220 +  mFlags &= NS_MATHML_OPERATOR_MUTABLE |
   1.221 +            NS_MATHML_OPERATOR_ACCENT | 
   1.222 +            NS_MATHML_OPERATOR_MOVABLELIMITS |
   1.223 +            NS_MATHML_OPERATOR_CENTERED |
   1.224 +            NS_MATHML_OPERATOR_INVISIBLE;
   1.225 +
   1.226 +  if (!mEmbellishData.coreFrame) {
   1.227 +    // i.e., we haven't been here before, the default form is infix
   1.228 +    form = NS_MATHML_OPERATOR_FORM_INFIX;
   1.229 +
   1.230 +    // reset everything so that we don't keep outdated values around
   1.231 +    // in case of dynamic changes
   1.232 +    mEmbellishData.flags = 0;
   1.233 +    mEmbellishData.coreFrame = nullptr;
   1.234 +    mEmbellishData.leadingSpace = 0;
   1.235 +    mEmbellishData.trailingSpace = 0;
   1.236 +    if (mMathMLChar.Length() != 1)
   1.237 +      mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;  
   1.238 +    // else... retain the native direction obtained in ProcessTextData()
   1.239 +
   1.240 +    if (!mFrames.FirstChild()) {
   1.241 +      return;
   1.242 +    }
   1.243 +
   1.244 +    mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
   1.245 +    mEmbellishData.coreFrame = this;
   1.246 +
   1.247 +    // there are two particular things that we also need to record so that if our
   1.248 +    // parent is <mover>, <munder>, or <munderover>, they will treat us properly:
   1.249 +    // 1) do we have accent="true"
   1.250 +    // 2) do we have movablelimits="true"
   1.251 +
   1.252 +    // they need the extra information to decide how to treat their scripts/limits
   1.253 +    // (note: <mover>, <munder>, or <munderover> need not necessarily be our
   1.254 +    // direct parent -- case of embellished operators)
   1.255 +
   1.256 +    // default values from the Operator Dictionary were obtained in ProcessTextData()
   1.257 +    // and these special bits are always kept in mFlags
   1.258 +    if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags))
   1.259 +      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
   1.260 +    if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags))
   1.261 +      mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
   1.262 +
   1.263 +    // see if the accent attribute is there
   1.264 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value);
   1.265 +    if (value.EqualsLiteral("true"))
   1.266 +      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
   1.267 +    else if (value.EqualsLiteral("false"))
   1.268 +      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT;
   1.269 +
   1.270 +    // see if the movablelimits attribute is there
   1.271 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::movablelimits_, value);
   1.272 +    if (value.EqualsLiteral("true"))
   1.273 +      mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
   1.274 +    else if (value.EqualsLiteral("false"))
   1.275 +      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS;
   1.276 +
   1.277 +     // ---------------------------------------------------------------------
   1.278 +     // we will be called again to re-sync the rest of our state next time...
   1.279 +     // (nobody needs the other values below at this stage)
   1.280 +     mFlags |= form;
   1.281 +     return;
   1.282 +  }
   1.283 +
   1.284 +  nsPresContext* presContext = PresContext();
   1.285 +
   1.286 +  // beware of bug 133814 - there is a two-way dependency in the
   1.287 +  // embellished hierarchy: our embellished ancestors need to set
   1.288 +  // their flags based on some of our state (set above), and here we
   1.289 +  // need to re-sync our 'form' depending on our outermost embellished
   1.290 +  // container. A null form here means that an earlier attempt to stretch
   1.291 +  // our mMathMLChar failed, in which case we don't bother re-stretching again
   1.292 +  if (form) {
   1.293 +    // get our outermost embellished container and its parent. 
   1.294 +    // (we ensure that we are the core, not just a sibling of the core)
   1.295 +    nsIFrame* embellishAncestor = this;
   1.296 +    nsEmbellishData embellishData;
   1.297 +    nsIFrame* parentAncestor = this;
   1.298 +    do {
   1.299 +      embellishAncestor = parentAncestor;
   1.300 +      parentAncestor = embellishAncestor->GetParent();
   1.301 +      GetEmbellishDataFrom(parentAncestor, embellishData);
   1.302 +    } while (embellishData.coreFrame == this);
   1.303 +
   1.304 +    // flag if we have an embellished ancestor
   1.305 +    if (embellishAncestor != this)
   1.306 +      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
   1.307 +    else
   1.308 +      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
   1.309 +
   1.310 +    // find the position of our outermost embellished container w.r.t
   1.311 +    // its siblings.
   1.312 +
   1.313 +    nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
   1.314 +    nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
   1.315 +
   1.316 +    // flag to distinguish from a real infix.  Set for (embellished) operators
   1.317 +    // that live in (inferred) mrows.
   1.318 +    nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
   1.319 +    bool zeroSpacing = false;
   1.320 +    if (mathAncestor) {
   1.321 +      zeroSpacing =  !mathAncestor->IsMrowLike();
   1.322 +    } else {
   1.323 +      nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
   1.324 +      if (blockFrame) {
   1.325 +        zeroSpacing = !blockFrame->IsMrowLike();
   1.326 +      }
   1.327 +    }
   1.328 +    if (zeroSpacing) {
   1.329 +      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
   1.330 +    } else {
   1.331 +      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
   1.332 +    }
   1.333 +
   1.334 +    // find our form
   1.335 +    form = NS_MATHML_OPERATOR_FORM_INFIX;
   1.336 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value);
   1.337 +    if (!value.IsEmpty()) {
   1.338 +      if (value.EqualsLiteral("prefix"))
   1.339 +        form = NS_MATHML_OPERATOR_FORM_PREFIX;
   1.340 +      else if (value.EqualsLiteral("postfix"))
   1.341 +        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
   1.342 +    }
   1.343 +    else {
   1.344 +      // set our form flag depending on the position
   1.345 +      if (!prevSibling && nextSibling)
   1.346 +        form = NS_MATHML_OPERATOR_FORM_PREFIX;
   1.347 +      else if (prevSibling && !nextSibling)
   1.348 +        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
   1.349 +    }
   1.350 +    mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits
   1.351 +    mFlags |= form;
   1.352 +
   1.353 +    // Use the default value suggested by the MathML REC.
   1.354 +    // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
   1.355 +    // thickmathspace = 5/18em
   1.356 +    float lspace = 5.0f/18.0f;
   1.357 +    float rspace = 5.0f/18.0f;
   1.358 +    // lookup the operator dictionary
   1.359 +    nsAutoString data;
   1.360 +    mMathMLChar.GetData(data);
   1.361 +    nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
   1.362 +    // Spacing is zero if our outermost embellished operator is not in an
   1.363 +    // inferred mrow.
   1.364 +    if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
   1.365 +        (lspace || rspace)) {
   1.366 +      // Cache the default values of lspace and rspace.
   1.367 +      // since these values are relative to the 'em' unit, convert to twips now
   1.368 +      nscoord em;
   1.369 +      nsRefPtr<nsFontMetrics> fm;
   1.370 +      nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   1.371 +      GetEmHeight(fm, em);
   1.372 +
   1.373 +      mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
   1.374 +      mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
   1.375 +
   1.376 +      // tuning if we don't want too much extra space when we are a script.
   1.377 +      // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
   1.378 +      // Our fonts can be anything, so...)
   1.379 +      if (StyleFont()->mScriptLevel > 0 &&
   1.380 +          !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
   1.381 +        mEmbellishData.leadingSpace /= 2;
   1.382 +        mEmbellishData.trailingSpace /= 2;
   1.383 +      }
   1.384 +    }
   1.385 +  }
   1.386 +
   1.387 +  // If we are an accent without explicit lspace="." or rspace=".",
   1.388 +  // we will ignore our default leading/trailing space
   1.389 +
   1.390 +  // lspace
   1.391 +  //
   1.392 +  // "Specifies the leading space appearing before the operator"
   1.393 +  //
   1.394 +  // values: length
   1.395 +  // default: set by dictionary (thickmathspace) 
   1.396 +  //
   1.397 +  // XXXfredw Support for negative and relative values is not implemented
   1.398 +  // (bug 805926).
   1.399 +  // Relative values will give a multiple of the current leading space,
   1.400 +  // which is not necessarily the default one.
   1.401 +  //
   1.402 +  nscoord leadingSpace = mEmbellishData.leadingSpace;
   1.403 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
   1.404 +  if (!value.IsEmpty()) {
   1.405 +    nsCSSValue cssValue;
   1.406 +    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
   1.407 +                                           mContent->OwnerDoc())) {
   1.408 +      if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
   1.409 +        leadingSpace = 0;
   1.410 +      else if (cssValue.IsLengthUnit())
   1.411 +        leadingSpace = CalcLength(presContext, mStyleContext, cssValue);
   1.412 +      mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
   1.413 +    }
   1.414 +  }
   1.415 +
   1.416 +  // rspace
   1.417 +  //
   1.418 +  // "Specifies the trailing space appearing after the operator"
   1.419 +  //
   1.420 +  // values: length
   1.421 +  // default: set by dictionary (thickmathspace) 
   1.422 +  //
   1.423 +  // XXXfredw Support for negative and relative values is not implemented
   1.424 +  // (bug 805926).
   1.425 +  // Relative values will give a multiple of the current leading space,
   1.426 +  // which is not necessarily the default one.
   1.427 +  //
   1.428 +  nscoord trailingSpace = mEmbellishData.trailingSpace;
   1.429 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rspace_, value);
   1.430 +  if (!value.IsEmpty()) {
   1.431 +    nsCSSValue cssValue;
   1.432 +    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
   1.433 +                                           mContent->OwnerDoc())) {
   1.434 +      if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
   1.435 +        trailingSpace = 0;
   1.436 +      else if (cssValue.IsLengthUnit())
   1.437 +        trailingSpace = CalcLength(presContext, mStyleContext, cssValue);
   1.438 +      mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
   1.439 +    }
   1.440 +  }
   1.441 +
   1.442 +  // little extra tuning to round lspace & rspace to at least a pixel so that
   1.443 +  // operators don't look as if they are colliding with their operands
   1.444 +  if (leadingSpace || trailingSpace) {
   1.445 +    nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
   1.446 +    if (leadingSpace && leadingSpace < onePixel)
   1.447 +      leadingSpace = onePixel;
   1.448 +    if (trailingSpace && trailingSpace < onePixel)
   1.449 +      trailingSpace = onePixel;
   1.450 +  }
   1.451 +
   1.452 +  // the values that we get from our attributes override the dictionary
   1.453 +  mEmbellishData.leadingSpace = leadingSpace;
   1.454 +  mEmbellishData.trailingSpace = trailingSpace;
   1.455 +
   1.456 +  // Now see if there are user-defined attributes that override the dictionary.
   1.457 +  // XXX If an attribute can be forced to be true when it is false in the
   1.458 +  // dictionary, then the following code has to change...
   1.459 +
   1.460 +  // For each attribute overriden by the user, turn off its bit flag.
   1.461 +  // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
   1.462 +  // special: accent and movablelimits are handled above,
   1.463 +  // don't process them here
   1.464 +
   1.465 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::stretchy_, value);
   1.466 +  if (value.EqualsLiteral("false")) {
   1.467 +    mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
   1.468 +  } else if (value.EqualsLiteral("true")) {
   1.469 +    mFlags |= NS_MATHML_OPERATOR_STRETCHY;
   1.470 +  }
   1.471 +  if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
   1.472 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::fence_, value);
   1.473 +    if (value.EqualsLiteral("false"))
   1.474 +      mFlags &= ~NS_MATHML_OPERATOR_FENCE;
   1.475 +  }
   1.476 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::largeop_, value);
   1.477 +  if (value.EqualsLiteral("false")) {
   1.478 +    mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
   1.479 +  } else if (value.EqualsLiteral("true")) {
   1.480 +    mFlags |= NS_MATHML_OPERATOR_LARGEOP;
   1.481 +  }
   1.482 +  if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
   1.483 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separator_, value);
   1.484 +    if (value.EqualsLiteral("false"))
   1.485 +      mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
   1.486 +  }
   1.487 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::symmetric_, value);
   1.488 +  if (value.EqualsLiteral("false"))
   1.489 +    mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
   1.490 +  else if (value.EqualsLiteral("true"))
   1.491 +    mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
   1.492 +
   1.493 +
   1.494 +  // minsize
   1.495 +  //
   1.496 +  // "Specifies the minimum size of the operator when stretchy"
   1.497 +  //
   1.498 +  // values: length
   1.499 +  // default: set by dictionary (1em)
   1.500 +  //
   1.501 +  // We don't allow negative values.
   1.502 +  // Note: Contrary to other "length" values, unitless and percentage do not
   1.503 +  // give a multiple of the defaut value but a multiple of the operator at
   1.504 +  // normal size.
   1.505 +  //
   1.506 +  mMinSize = 0;
   1.507 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::minsize_, value);
   1.508 +  if (!value.IsEmpty()) {
   1.509 +    nsCSSValue cssValue;
   1.510 +    if (nsMathMLElement::ParseNumericValue(value, cssValue,
   1.511 +                                           nsMathMLElement::
   1.512 +                                           PARSE_ALLOW_UNITLESS,
   1.513 +                                           mContent->OwnerDoc())) {
   1.514 +      nsCSSUnit unit = cssValue.GetUnit();
   1.515 +      if (eCSSUnit_Number == unit)
   1.516 +        mMinSize = cssValue.GetFloatValue();
   1.517 +      else if (eCSSUnit_Percent == unit)
   1.518 +        mMinSize = cssValue.GetPercentValue();
   1.519 +      else if (eCSSUnit_Null != unit) {
   1.520 +        mMinSize = float(CalcLength(presContext, mStyleContext, cssValue));
   1.521 +        mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
   1.522 +      }
   1.523 +    }
   1.524 +  }
   1.525 +
   1.526 +  // maxsize
   1.527 +  //
   1.528 +  // "Specifies the maximum size of the operator when stretchy"
   1.529 +  //
   1.530 +  // values: length | "infinity"
   1.531 +  // default: set by dictionary (infinity)
   1.532 +  //
   1.533 +  // We don't allow negative values.
   1.534 +  // Note: Contrary to other "length" values, unitless and percentage do not
   1.535 +  // give a multiple of the defaut value but a multiple of the operator at
   1.536 +  // normal size.
   1.537 +  //
   1.538 +  mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
   1.539 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::maxsize_, value);
   1.540 +  if (!value.IsEmpty()) {
   1.541 +    nsCSSValue cssValue;
   1.542 +    if (nsMathMLElement::ParseNumericValue(value, cssValue,
   1.543 +                                           nsMathMLElement::
   1.544 +                                           PARSE_ALLOW_UNITLESS,
   1.545 +                                           mContent->OwnerDoc())) {
   1.546 +      nsCSSUnit unit = cssValue.GetUnit();
   1.547 +      if (eCSSUnit_Number == unit)
   1.548 +        mMaxSize = cssValue.GetFloatValue();
   1.549 +      else if (eCSSUnit_Percent == unit)
   1.550 +        mMaxSize = cssValue.GetPercentValue();
   1.551 +      else if (eCSSUnit_Null != unit) {
   1.552 +        mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue));
   1.553 +        mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
   1.554 +      }
   1.555 +    }
   1.556 +  }
   1.557 +}
   1.558 +
   1.559 +static uint32_t
   1.560 +GetStretchHint(nsOperatorFlags aFlags, nsPresentationData aPresentationData,
   1.561 +               bool aIsVertical, const nsStyleFont* aStyleFont)
   1.562 +{
   1.563 +  uint32_t stretchHint = NS_STRETCH_NONE;
   1.564 +  // See if it is okay to stretch,
   1.565 +  // starting from what the Operator Dictionary said
   1.566 +  if (NS_MATHML_OPERATOR_IS_MUTABLE(aFlags)) {
   1.567 +    // set the largeop or largeopOnly flags to suitably cover all the
   1.568 +    // 8 possible cases depending on whether displaystyle, largeop,
   1.569 +    // stretchy are true or false (see bug 69325).
   1.570 +    // . largeopOnly is taken if largeop=true and stretchy=false
   1.571 +    // . largeop is taken if largeop=true and stretchy=true
   1.572 +    if (aStyleFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
   1.573 +        NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
   1.574 +      stretchHint = NS_STRETCH_LARGEOP; // (largeopOnly, not mask!)
   1.575 +      if (NS_MATHML_OPERATOR_IS_INTEGRAL(aFlags)) {
   1.576 +        stretchHint |= NS_STRETCH_INTEGRAL;
   1.577 +      }
   1.578 +      if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
   1.579 +        stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
   1.580 +      }
   1.581 +    }
   1.582 +    else if(NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
   1.583 +      if (aIsVertical) {
   1.584 +        // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
   1.585 +        stretchHint = NS_STRETCH_NEARER;
   1.586 +      }
   1.587 +      else {
   1.588 +        stretchHint = NS_STRETCH_NORMAL;
   1.589 +      }
   1.590 +    }
   1.591 +    // else if the stretchy and largeop attributes have been disabled,
   1.592 +    // the operator is not mutable
   1.593 +  }
   1.594 +  return stretchHint;
   1.595 +}
   1.596 +
   1.597 +// NOTE: aDesiredStretchSize is an IN/OUT parameter
   1.598 +//       On input  - it contains our current size
   1.599 +//       On output - the same size or the new size that we want
   1.600 +NS_IMETHODIMP
   1.601 +nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext,
   1.602 +                         nsStretchDirection   aStretchDirection,
   1.603 +                         nsBoundingMetrics&   aContainerSize,
   1.604 +                         nsHTMLReflowMetrics& aDesiredStretchSize)
   1.605 +{
   1.606 +  if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
   1.607 +    NS_WARNING("it is wrong to fire stretch more than once on a frame");
   1.608 +    return NS_OK;
   1.609 +  }
   1.610 +  mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
   1.611 +
   1.612 +  nsIFrame* firstChild = mFrames.FirstChild();
   1.613 +
   1.614 +  // get the axis height;
   1.615 +  nsRefPtr<nsFontMetrics> fm;
   1.616 +  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   1.617 +  aRenderingContext.SetFont(fm);
   1.618 +  nscoord axisHeight, height;
   1.619 +  GetAxisHeight(aRenderingContext, fm, axisHeight);
   1.620 +
   1.621 +  // get the leading to be left at the top and the bottom of the stretched char
   1.622 +  // this seems more reliable than using fm->GetLeading() on suspicious fonts
   1.623 +  nscoord em;
   1.624 +  GetEmHeight(fm, em);
   1.625 +  nscoord leading = NSToCoordRound(0.2f * em);
   1.626 +
   1.627 +  // Operators that are stretchy, or those that are to be centered
   1.628 +  // to cater for fonts that are not math-aware, are handled by the MathMLChar
   1.629 +  // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time)
   1.630 +  bool useMathMLChar = UseMathMLChar();
   1.631 +
   1.632 +  nsBoundingMetrics charSize;
   1.633 +  nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
   1.634 +  bool isVertical = false;
   1.635 +
   1.636 +  if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
   1.637 +       (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT))  &&
   1.638 +      (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
   1.639 +    isVertical = true;
   1.640 +  }
   1.641 +
   1.642 +  uint32_t stretchHint =
   1.643 +    GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());
   1.644 +
   1.645 +  if (useMathMLChar) {
   1.646 +    nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
   1.647 +
   1.648 +    if (stretchHint != NS_STRETCH_NONE) {
   1.649 +
   1.650 +      container = aContainerSize;
   1.651 +
   1.652 +      // some adjustments if the operator is symmetric and vertical
   1.653 +
   1.654 +      if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
   1.655 +        // we need to center about the axis
   1.656 +        nscoord delta = std::max(container.ascent - axisHeight,
   1.657 +                               container.descent + axisHeight);
   1.658 +        container.ascent = delta + axisHeight;
   1.659 +        container.descent = delta - axisHeight;
   1.660 +
   1.661 +        // get ready in case we encounter user-desired min-max size
   1.662 +        delta = std::max(initialSize.ascent - axisHeight,
   1.663 +                       initialSize.descent + axisHeight);
   1.664 +        initialSize.ascent = delta + axisHeight;
   1.665 +        initialSize.descent = delta - axisHeight;
   1.666 +      }
   1.667 +
   1.668 +      // check for user-desired min-max size
   1.669 +
   1.670 +      if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
   1.671 +        // if we are here, there is a user defined maxsize ...
   1.672 +        //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible?
   1.673 +        if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
   1.674 +          // there is an explicit value like maxsize="20pt"
   1.675 +          // try to maintain the aspect ratio of the char
   1.676 +          float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent);
   1.677 +          container.ascent =
   1.678 +            std::min(container.ascent, nscoord(initialSize.ascent * aspect));
   1.679 +          container.descent =
   1.680 +            std::min(container.descent, nscoord(initialSize.descent * aspect));
   1.681 +          // below we use a type cast instead of a conversion to avoid a VC++ bug
   1.682 +          // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
   1.683 +          container.width =
   1.684 +            std::min(container.width, (nscoord)mMaxSize);
   1.685 +        }
   1.686 +        else { // multiplicative value
   1.687 +          container.ascent =
   1.688 +            std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize));
   1.689 +          container.descent =
   1.690 +            std::min(container.descent, nscoord(initialSize.descent * mMaxSize));
   1.691 +          container.width =
   1.692 +            std::min(container.width, nscoord(initialSize.width * mMaxSize));
   1.693 +        }
   1.694 +
   1.695 +        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
   1.696 +          // re-adjust to align the char with the bottom of the initial container
   1.697 +          height = container.ascent + container.descent;
   1.698 +          container.descent = aContainerSize.descent;
   1.699 +          container.ascent = height - container.descent;
   1.700 +        }
   1.701 +      }
   1.702 +
   1.703 +      if (mMinSize > 0.0f) {
   1.704 +        // if we are here, there is a user defined minsize ...
   1.705 +        // always allow the char to stretch in its natural direction,
   1.706 +        // even if it is different from the caller's direction 
   1.707 +        if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
   1.708 +            aStretchDirection != mEmbellishData.direction) {
   1.709 +          aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
   1.710 +          // but when we are not honoring the requested direction
   1.711 +          // we should not use the caller's container size either
   1.712 +          container = initialSize;
   1.713 +        }
   1.714 +        if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
   1.715 +          // there is an explicit value like minsize="20pt"
   1.716 +          // try to maintain the aspect ratio of the char
   1.717 +          float aspect = mMinSize / float(initialSize.ascent + initialSize.descent);
   1.718 +          container.ascent =
   1.719 +            std::max(container.ascent, nscoord(initialSize.ascent * aspect));
   1.720 +          container.descent =
   1.721 +            std::max(container.descent, nscoord(initialSize.descent * aspect));
   1.722 +          container.width =
   1.723 +            std::max(container.width, (nscoord)mMinSize);
   1.724 +        }
   1.725 +        else { // multiplicative value
   1.726 +          container.ascent =
   1.727 +            std::max(container.ascent, nscoord(initialSize.ascent * mMinSize));
   1.728 +          container.descent =
   1.729 +            std::max(container.descent, nscoord(initialSize.descent * mMinSize));
   1.730 +          container.width =
   1.731 +            std::max(container.width, nscoord(initialSize.width * mMinSize));
   1.732 +        }
   1.733 +
   1.734 +        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
   1.735 +          // re-adjust to align the char with the bottom of the initial container
   1.736 +          height = container.ascent + container.descent;
   1.737 +          container.descent = aContainerSize.descent;
   1.738 +          container.ascent = height - container.descent;
   1.739 +        }
   1.740 +      }
   1.741 +    }
   1.742 +
   1.743 +    // let the MathMLChar stretch itself...
   1.744 +    nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext,
   1.745 +                                       aStretchDirection, container, charSize,
   1.746 +                                       stretchHint,
   1.747 +                                       StyleVisibility()->mDirection);
   1.748 +    if (NS_FAILED(res)) {
   1.749 +      // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
   1.750 +      // clear our 'form' to behave as if the operator wasn't in the dictionary
   1.751 +      mFlags &= ~NS_MATHML_OPERATOR_FORM;
   1.752 +      useMathMLChar = false;
   1.753 +    }
   1.754 +  }
   1.755 +
   1.756 +  // Place our children using the default method
   1.757 +  // This will allow our child text frame to get its DidReflow()
   1.758 +  nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
   1.759 +  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   1.760 +    // Make sure the child frames get their DidReflow() calls.
   1.761 +    DidReflowChildren(mFrames.FirstChild());
   1.762 +  }
   1.763 +
   1.764 +  if (useMathMLChar) {
   1.765 +    // update our bounding metrics... it becomes that of our MathML char
   1.766 +    mBoundingMetrics = charSize;
   1.767 +
   1.768 +    // if the returned direction is 'unsupported', the char didn't actually change. 
   1.769 +    // So we do the centering only if necessary
   1.770 +    if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED ||
   1.771 +        NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
   1.772 +
   1.773 +      bool largeopOnly =
   1.774 +        (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
   1.775 +        (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;
   1.776 +
   1.777 +      if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
   1.778 +        // the desired size returned by mMathMLChar maybe different
   1.779 +        // from the size of the container.
   1.780 +        // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
   1.781 +
   1.782 +        height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
   1.783 +        if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) ||
   1.784 +            NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
   1.785 +          // For symmetric and vertical operators, or for operators that are always
   1.786 +          // centered ('+', '*', etc) we want to center about the axis of the container
   1.787 +          mBoundingMetrics.descent = height/2 - axisHeight;
   1.788 +        } else if (!largeopOnly) {
   1.789 +          // Align the center of the char with the center of the container
   1.790 +          mBoundingMetrics.descent = height/2 +
   1.791 +            (container.ascent + container.descent)/2 - container.ascent;
   1.792 +        } // else align the baselines
   1.793 +        mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
   1.794 +      }
   1.795 +    }
   1.796 +  }
   1.797 +
   1.798 +  // Fixup for the final height.
   1.799 +  // On one hand, our stretchy height can sometimes be shorter than surrounding
   1.800 +  // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading|
   1.801 +  // that is smaller than the ASCII's ascent, hence when painting the background
   1.802 +  // later, it won't look uniform along the line.
   1.803 +  // On the other hand, sometimes we may leave too much gap when our glyph happens
   1.804 +  // to come from a font with tall glyphs. For example, since CMEX10 has very tall
   1.805 +  // glyphs, its natural font metrics are large, even if we pick a small glyph
   1.806 +  // whose size is comparable to the size of a normal ASCII glyph.
   1.807 +  // So to avoid uneven spacing in either of these two cases, we use the height
   1.808 +  // of the ASCII font as a reference and try to match it if possible.
   1.809 +
   1.810 +  // special case for accents... keep them short to improve mouse operations...
   1.811 +  // an accent can only be the non-first child of <mover>, <munder>, <munderover>
   1.812 +  bool isAccent =
   1.813 +    NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags);
   1.814 +  if (isAccent) {
   1.815 +    nsEmbellishData parentData;
   1.816 +    GetEmbellishDataFrom(mParent, parentData);
   1.817 +    isAccent =
   1.818 +       (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) ||
   1.819 +        NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) &&
   1.820 +       parentData.coreFrame != this;
   1.821 +  }
   1.822 +  if (isAccent && firstChild) {
   1.823 +    // see bug 188467 for what is going on here
   1.824 +    nscoord dy = aDesiredStretchSize.TopAscent() - (mBoundingMetrics.ascent + leading);
   1.825 +    aDesiredStretchSize.SetTopAscent(mBoundingMetrics.ascent + leading);
   1.826 +    aDesiredStretchSize.Height() = aDesiredStretchSize.TopAscent() + mBoundingMetrics.descent;
   1.827 +
   1.828 +    firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
   1.829 +  }
   1.830 +  else if (useMathMLChar) {
   1.831 +    nscoord ascent = fm->MaxAscent();
   1.832 +    nscoord descent = fm->MaxDescent();
   1.833 +    aDesiredStretchSize.SetTopAscent(std::max(mBoundingMetrics.ascent + leading, ascent));
   1.834 +    aDesiredStretchSize.Height() = aDesiredStretchSize.TopAscent() +
   1.835 +                                 std::max(mBoundingMetrics.descent + leading, descent);
   1.836 +  }
   1.837 +  aDesiredStretchSize.Width() = mBoundingMetrics.width;
   1.838 +  aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
   1.839 +  mReference.x = 0;
   1.840 +  mReference.y = aDesiredStretchSize.TopAscent();
   1.841 +  // Place our mMathMLChar, its origin is in our coordinate system
   1.842 +  if (useMathMLChar) {
   1.843 +    nscoord dy = aDesiredStretchSize.TopAscent() - mBoundingMetrics.ascent;
   1.844 +    mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
   1.845 +  }
   1.846 +
   1.847 +  // Before we leave... there is a last item in the check-list:
   1.848 +  // If our parent is not embellished, it means we are the outermost embellished
   1.849 +  // container and so we put the spacing, otherwise we don't include the spacing,
   1.850 +  // the outermost embellished container will take care of it.
   1.851 +
   1.852 +  if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
   1.853 +
   1.854 +    // Account the spacing if we are not an accent with explicit attributes
   1.855 +    nscoord leadingSpace = mEmbellishData.leadingSpace;
   1.856 +    if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
   1.857 +      leadingSpace = 0;
   1.858 +    }
   1.859 +    nscoord trailingSpace = mEmbellishData.trailingSpace;
   1.860 +    if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
   1.861 +      trailingSpace = 0;
   1.862 +    }
   1.863 +
   1.864 +    mBoundingMetrics.width += leadingSpace + trailingSpace;
   1.865 +    aDesiredStretchSize.Width() = mBoundingMetrics.width;
   1.866 +    aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
   1.867 +
   1.868 +    nscoord dx = (StyleVisibility()->mDirection ?
   1.869 +                  trailingSpace : leadingSpace);
   1.870 +    if (dx) {
   1.871 +      // adjust the offsets
   1.872 +      mBoundingMetrics.leftBearing += dx;
   1.873 +      mBoundingMetrics.rightBearing += dx;
   1.874 +      aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
   1.875 +      aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
   1.876 +
   1.877 +      if (useMathMLChar) {
   1.878 +        nsRect rect;
   1.879 +        mMathMLChar.GetRect(rect);
   1.880 +        mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y,
   1.881 +                                   rect.width, rect.height));
   1.882 +      }
   1.883 +      else {
   1.884 +        nsIFrame* childFrame = firstChild;
   1.885 +        while (childFrame) {
   1.886 +          childFrame->SetPosition(childFrame->GetPosition() +
   1.887 +                                  nsPoint(dx, 0));
   1.888 +          childFrame = childFrame->GetNextSibling();
   1.889 +        }
   1.890 +      }
   1.891 +    }
   1.892 +  }
   1.893 +
   1.894 +  // Finished with these:
   1.895 +  ClearSavedChildMetrics();
   1.896 +  // Set our overflow area
   1.897 +  GatherAndStoreOverflow(&aDesiredStretchSize);
   1.898 +
   1.899 +  // There used to be code here to change the height of the child frame to
   1.900 +  // change the caret height, but the text frame that manages the caret is now
   1.901 +  // not a direct child but wrapped in a block frame.  See also bug 412033.
   1.902 +
   1.903 +  return NS_OK;
   1.904 +}
   1.905 +
   1.906 +NS_IMETHODIMP
   1.907 +nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent)
   1.908 +{
   1.909 +  // retain our native direction, it only changes if our text content changes
   1.910 +  nsStretchDirection direction = mEmbellishData.direction;
   1.911 +  nsMathMLTokenFrame::InheritAutomaticData(aParent);
   1.912 +  ProcessTextData();
   1.913 +  mEmbellishData.direction = direction;
   1.914 +  return NS_OK;
   1.915 +}
   1.916 +
   1.917 +NS_IMETHODIMP
   1.918 +nsMathMLmoFrame::TransmitAutomaticData()
   1.919 +{
   1.920 +  // this will cause us to re-sync our flags from scratch
   1.921 +  // but our returned 'form' is still not final (bug 133429), it will
   1.922 +  // be recomputed to its final value during the next call in Reflow()
   1.923 +  mEmbellishData.coreFrame = nullptr;
   1.924 +  ProcessOperatorData();
   1.925 +  return NS_OK;
   1.926 +}
   1.927 +
   1.928 +nsresult
   1.929 +nsMathMLmoFrame::SetInitialChildList(ChildListID     aListID,
   1.930 +                                     nsFrameList&    aChildList)
   1.931 +{
   1.932 +  // First, let the parent class do its work
   1.933 +  nsresult rv = nsMathMLTokenFrame::SetInitialChildList(aListID, aChildList);
   1.934 +  if (NS_FAILED(rv))
   1.935 +    return rv;
   1.936 +
   1.937 +  ProcessTextData();
   1.938 +  return rv;
   1.939 +}
   1.940 +
   1.941 +nsresult
   1.942 +nsMathMLmoFrame::Reflow(nsPresContext*          aPresContext,
   1.943 +                        nsHTMLReflowMetrics&     aDesiredSize,
   1.944 +                        const nsHTMLReflowState& aReflowState,
   1.945 +                        nsReflowStatus&          aStatus)
   1.946 +{
   1.947 +  // certain values use units that depend on our style context, so
   1.948 +  // it is safer to just process the whole lot here
   1.949 +  ProcessOperatorData();
   1.950 +
   1.951 +  return nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize,
   1.952 +                                    aReflowState, aStatus);
   1.953 +}
   1.954 +
   1.955 +/* virtual */ void
   1.956 +nsMathMLmoFrame::MarkIntrinsicWidthsDirty()
   1.957 +{
   1.958 +  // if we get this, it may mean that something changed in the text
   1.959 +  // content. So blow away everything an re-build the automatic data
   1.960 +  // from the parent of our outermost embellished container (we ensure
   1.961 +  // that we are the core, not just a sibling of the core)
   1.962 +
   1.963 +  ProcessTextData();
   1.964 +
   1.965 +  nsIFrame* target = this;
   1.966 +  nsEmbellishData embellishData;
   1.967 +  do {
   1.968 +    target = target->GetParent();
   1.969 +    GetEmbellishDataFrom(target, embellishData);
   1.970 +  } while (embellishData.coreFrame == this);
   1.971 +
   1.972 +  // we have automatic data to update in the children of the target frame
   1.973 +  // XXXldb This should really be marking dirty rather than rebuilding
   1.974 +  // so that we don't rebuild multiple times for the same change.
   1.975 +  RebuildAutomaticDataForChildren(target);
   1.976 +
   1.977 +  nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
   1.978 +}
   1.979 +
   1.980 +/* virtual */ void
   1.981 +nsMathMLmoFrame::GetIntrinsicWidthMetrics(nsRenderingContext *aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
   1.982 +{
   1.983 +  ProcessOperatorData();
   1.984 +  if (UseMathMLChar()) {
   1.985 +    uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true,
   1.986 +                                          StyleFont());
   1.987 +    aDesiredSize.Width() = mMathMLChar.
   1.988 +      GetMaxWidth(PresContext(), *aRenderingContext,
   1.989 +                  stretchHint, mMaxSize,
   1.990 +                  NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags));
   1.991 +  }
   1.992 +  else {
   1.993 +    nsMathMLTokenFrame::GetIntrinsicWidthMetrics(aRenderingContext,
   1.994 +                                                 aDesiredSize);
   1.995 +  }
   1.996 +
   1.997 +  // leadingSpace and trailingSpace are actually applied to the outermost
   1.998 +  // embellished container but for determining total intrinsic width it should
   1.999 +  // be safe to include it for the core here instead.
  1.1000 +  bool isRTL = StyleVisibility()->mDirection;
  1.1001 +  aDesiredSize.Width() +=
  1.1002 +    mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
  1.1003 +  aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
  1.1004 +  if (isRTL) {
  1.1005 +    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
  1.1006 +    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
  1.1007 +  } else {
  1.1008 +    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
  1.1009 +    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
  1.1010 +  }
  1.1011 +}
  1.1012 +
  1.1013 +nsresult
  1.1014 +nsMathMLmoFrame::AttributeChanged(int32_t         aNameSpaceID,
  1.1015 +                                  nsIAtom*        aAttribute,
  1.1016 +                                  int32_t         aModType)
  1.1017 +{
  1.1018 +  // check if this is an attribute that can affect the embellished hierarchy
  1.1019 +  // in a significant way and re-layout the entire hierarchy.
  1.1020 +  if (nsGkAtoms::accent_ == aAttribute ||
  1.1021 +      nsGkAtoms::movablelimits_ == aAttribute) {
  1.1022 +
  1.1023 +    // set the target as the parent of our outermost embellished container
  1.1024 +    // (we ensure that we are the core, not just a sibling of the core)
  1.1025 +    nsIFrame* target = this;
  1.1026 +    nsEmbellishData embellishData;
  1.1027 +    do {
  1.1028 +      target = target->GetParent();
  1.1029 +      GetEmbellishDataFrom(target, embellishData);
  1.1030 +    } while (embellishData.coreFrame == this);
  1.1031 +
  1.1032 +    // we have automatic data to update in the children of the target frame
  1.1033 +    return ReLayoutChildren(target);
  1.1034 +  }
  1.1035 +
  1.1036 +  return nsMathMLTokenFrame::
  1.1037 +         AttributeChanged(aNameSpaceID, aAttribute, aModType);
  1.1038 +}
  1.1039 +
  1.1040 +// ----------------------
  1.1041 +// No need to track the style context given to our MathML char. 
  1.1042 +// the Style System will use these to pass the proper style context to our MathMLChar
  1.1043 +nsStyleContext*
  1.1044 +nsMathMLmoFrame::GetAdditionalStyleContext(int32_t aIndex) const
  1.1045 +{
  1.1046 +  switch (aIndex) {
  1.1047 +  case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
  1.1048 +    return mMathMLChar.GetStyleContext();
  1.1049 +  default:
  1.1050 +    return nullptr;
  1.1051 +  }
  1.1052 +}
  1.1053 +
  1.1054 +void
  1.1055 +nsMathMLmoFrame::SetAdditionalStyleContext(int32_t          aIndex,
  1.1056 +                                           nsStyleContext*  aStyleContext)
  1.1057 +{
  1.1058 +  switch (aIndex) {
  1.1059 +  case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
  1.1060 +    mMathMLChar.SetStyleContext(aStyleContext);
  1.1061 +    break;
  1.1062 +  }
  1.1063 +}

mercurial