Exemple #1
0
bool
nsMathMLmoFrame::UseMathMLChar()
{
  return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
          NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
    NS_MATHML_OPERATOR_IS_CENTERED(mFlags);
}
bool
nsMathMLOperators::LookupOperator(const nsString&       aOperator,
                                  const nsOperatorFlags aForm,
                                  nsOperatorFlags*      aFlags,
                                  float*                aLeadingSpace,
                                  float*                aTrailingSpace)
{
  if (!gGlobalsInitialized) {
    InitGlobals();
  }
  if (gOperatorTable) {
    NS_ASSERTION(aFlags && aLeadingSpace && aTrailingSpace, "bad usage");
    NS_ASSERTION(aForm > 0 && aForm < 4, "*** invalid call ***");

    // The MathML REC says:
    // If the operator does not occur in the dictionary with the specified form,
    // the renderer should use one of the forms which is available there, in the
    // order of preference: infix, postfix, prefix.

    OperatorData* found;
    int32_t form = NS_MATHML_OPERATOR_GET_FORM(aForm);
    if (!(found = GetOperatorData(aOperator, form))) {
      if (form == NS_MATHML_OPERATOR_FORM_INFIX ||
          !(found =
            GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) {
        if (form == NS_MATHML_OPERATOR_FORM_POSTFIX ||
            !(found =
              GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX))) {
          if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
            found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
          }
        }
      }
    }
    if (found) {
      NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
      *aLeadingSpace = found->mLeadingSpace;
      *aTrailingSpace = found->mTrailingSpace;
      *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits
      *aFlags |= found->mFlags; // just add bits without overwriting
      return true;
    }
  }
  return false;
}
// get our 'form' and lookup in the Operator Dictionary to fetch 
// our default data that may come from there. Then complete our setup
// using attributes that we may have. To stay in sync, this function is
// called very often. We depend on many things that may change around us.
// However, we re-use unchanged values.
void
nsMathMLmoFrame::ProcessOperatorData()
{
  // if we have been here before, we will just use our cached form
  nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
  nsAutoString value;

  // special bits are always kept in mFlags.
  // remember the mutable bit from ProcessTextData().
  // Some chars are listed under different forms in the dictionary,
  // and there could be a form under which the char is mutable.
  // If the char is the core of an embellished container, we will keep
  // it mutable irrespective of the form of the embellished container.
  // Also remember the other special bits that we want to carry forward.
  mFlags &= NS_MATHML_OPERATOR_MUTABLE |
            NS_MATHML_OPERATOR_ACCENT | 
            NS_MATHML_OPERATOR_MOVABLELIMITS |
            NS_MATHML_OPERATOR_CENTERED |
            NS_MATHML_OPERATOR_INVISIBLE;

  if (!mEmbellishData.coreFrame) {
    // i.e., we haven't been here before, the default form is infix
    form = NS_MATHML_OPERATOR_FORM_INFIX;

    // reset everything so that we don't keep outdated values around
    // in case of dynamic changes
    mEmbellishData.flags = 0;
    mEmbellishData.coreFrame = nullptr;
    mEmbellishData.leadingSpace = 0;
    mEmbellishData.trailingSpace = 0;
    if (mMathMLChar.Length() != 1)
      mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;  
    // else... retain the native direction obtained in ProcessTextData()

    if (!mFrames.FirstChild()) {
      return;
    }

    mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
    mEmbellishData.coreFrame = this;

    // there are two particular things that we also need to record so that if our
    // parent is <mover>, <munder>, or <munderover>, they will treat us properly:
    // 1) do we have accent="true"
    // 2) do we have movablelimits="true"

    // they need the extra information to decide how to treat their scripts/limits
    // (note: <mover>, <munder>, or <munderover> need not necessarily be our
    // direct parent -- case of embellished operators)

    // default values from the Operator Dictionary were obtained in ProcessTextData()
    // and these special bits are always kept in mFlags
    if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags))
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
    if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags))
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;

    // see if the accent attribute is there
    GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::accent_,
                 value);
    if (value.EqualsLiteral("true"))
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
    else if (value.EqualsLiteral("false"))
      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT;

    // see if the movablelimits attribute is there
    GetAttribute(mContent, mPresentationData.mstyle,
                 nsGkAtoms::movablelimits_, value);
    if (value.EqualsLiteral("true"))
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
    else if (value.EqualsLiteral("false"))
      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS;

     // ---------------------------------------------------------------------
     // we will be called again to re-sync the rest of our state next time...
     // (nobody needs the other values below at this stage)
     mFlags |= form;
     return;
  }

  nsPresContext* presContext = PresContext();

  // beware of bug 133814 - there is a two-way dependency in the
  // embellished hierarchy: our embellished ancestors need to set
  // their flags based on some of our state (set above), and here we
  // need to re-sync our 'form' depending on our outermost embellished
  // container. A null form here means that an earlier attempt to stretch
  // our mMathMLChar failed, in which case we don't bother re-stretching again
  if (form) {
    // get our outermost embellished container and its parent. 
    // (we ensure that we are the core, not just a sibling of the core)
    nsIFrame* embellishAncestor = this;
    nsEmbellishData embellishData;
    nsIFrame* parentAncestor = this;
    do {
      embellishAncestor = parentAncestor;
      parentAncestor = embellishAncestor->GetParent();
      GetEmbellishDataFrom(parentAncestor, embellishData);
    } while (embellishData.coreFrame == this);

    // flag if we have an embellished ancestor
    if (embellishAncestor != this)
      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
    else
      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;

    // find the position of our outermost embellished container w.r.t
    // its siblings.

    nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
    nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();

    // flag to distinguish from a real infix
    if (!prevSibling && !nextSibling)
      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
    else
      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;

    // find our form
    form = NS_MATHML_OPERATOR_FORM_INFIX;
    GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::form,
                 value);
    if (!value.IsEmpty()) {
      if (value.EqualsLiteral("prefix"))
        form = NS_MATHML_OPERATOR_FORM_PREFIX;
      else if (value.EqualsLiteral("postfix"))
        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
    }
    else {
      // set our form flag depending on the position
      if (!prevSibling && nextSibling)
        form = NS_MATHML_OPERATOR_FORM_PREFIX;
      else if (prevSibling && !nextSibling)
        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
    }
    mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits
    mFlags |= form;

    // Use the default value suggested by the MathML REC.
    // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
    // thickmathspace = 5/18em
    float lspace = 5.0f/18.0f;
    float rspace = 5.0f/18.0f;
    if (NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)) {
      // mMathMLChar has been reset in ProcessTextData so we can not find it
      // in the operator dictionary. The operator dictionary always uses
      // lspace = rspace = 0 for invisible operators.
      lspace = rspace = 0;
    } else {
      // lookup the operator dictionary
      nsAutoString data;
      mMathMLChar.GetData(data);
      nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
    }
    if (lspace || rspace) {
      // Cache the default values of lspace and rspace.
      // since these values are relative to the 'em' unit, convert to twips now
      nscoord em;
      nsRefPtr<nsFontMetrics> fm;
      nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
      GetEmHeight(fm, em);

      mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
      mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);

      // tuning if we don't want too much extra space when we are a script.
      // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
      // Our fonts can be anything, so...)
      if (StyleFont()->mScriptLevel > 0) {
        if (NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags)) {
          // could be an isolated accent or script, e.g., x^{+}, just zero out
          mEmbellishData.leadingSpace = 0;
          mEmbellishData.trailingSpace  = 0;
        }
        else if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
          mEmbellishData.leadingSpace /= 2;
          mEmbellishData.trailingSpace  /= 2;
        }
      }
    }
  }

  // If we are an accent without explicit lspace="." or rspace=".",
  // we will ignore our default leading/trailing space

  // lspace
  //
  // "Specifies the leading space appearing before the operator"
  //
  // values: length
  // default: set by dictionary (thickmathspace) 
  //
  // XXXfredw Support for negative and relative values is not implemented
  // (bug 805926).
  // Relative values will give a multiple of the current leading space,
  // which is not necessarily the default one.
  //
  nscoord leadingSpace = mEmbellishData.leadingSpace;
  GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::lspace_,
               value);
  if (!value.IsEmpty()) {
    nsCSSValue cssValue;
    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
                                           mContent->OwnerDoc())) {
      if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
        leadingSpace = 0;
      else if (cssValue.IsLengthUnit())
        leadingSpace = CalcLength(presContext, mStyleContext, cssValue);
      mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
    }
  }

  // rspace
  //
  // "Specifies the trailing space appearing after the operator"
  //
  // values: length
  // default: set by dictionary (thickmathspace) 
  //
  // XXXfredw Support for negative and relative values is not implemented
  // (bug 805926).
  // Relative values will give a multiple of the current leading space,
  // which is not necessarily the default one.
  //
  nscoord trailingSpace = mEmbellishData.trailingSpace;
  GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::rspace_,
               value);
  if (!value.IsEmpty()) {
    nsCSSValue cssValue;
    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
                                           mContent->OwnerDoc())) {
      if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
        trailingSpace = 0;
      else if (cssValue.IsLengthUnit())
        trailingSpace = CalcLength(presContext, mStyleContext, cssValue);
      mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
    }
  }

  // little extra tuning to round lspace & rspace to at least a pixel so that
  // operators don't look as if they are colliding with their operands
  if (leadingSpace || trailingSpace) {
    nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
    if (leadingSpace && leadingSpace < onePixel)
      leadingSpace = onePixel;
    if (trailingSpace && trailingSpace < onePixel)
      trailingSpace = onePixel;
  }

  // the values that we get from our attributes override the dictionary
  mEmbellishData.leadingSpace = leadingSpace;
  mEmbellishData.trailingSpace = trailingSpace;

  // Now see if there are user-defined attributes that override the dictionary.
  // XXX If an attribute can be forced to be true when it is false in the
  // dictionary, then the following code has to change...

  // For each attribute overriden by the user, turn off its bit flag.
  // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
  // special: accent and movablelimits are handled above,
  // don't process them here

  GetAttribute(mContent, mPresentationData.mstyle,
               nsGkAtoms::stretchy_, value);
  if (value.EqualsLiteral("false")) {
    mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
  } else if (value.EqualsLiteral("true")) {
    mFlags |= NS_MATHML_OPERATOR_STRETCHY;
  }
  if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
    GetAttribute(mContent, mPresentationData.mstyle,
                 nsGkAtoms::fence_, value);
    if (value.EqualsLiteral("false"))
      mFlags &= ~NS_MATHML_OPERATOR_FENCE;
  }
  GetAttribute(mContent, mPresentationData.mstyle,
               nsGkAtoms::largeop_, value);
  if (value.EqualsLiteral("false")) {
    mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
  } else if (value.EqualsLiteral("true")) {
    mFlags |= NS_MATHML_OPERATOR_LARGEOP;
  }
  if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
    GetAttribute(mContent, mPresentationData.mstyle,
                 nsGkAtoms::separator_, value);
    if (value.EqualsLiteral("false"))
      mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
  }
  GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::symmetric_,
               value);
  if (value.EqualsLiteral("false"))
    mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
  else if (value.EqualsLiteral("true"))
    mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;


  // minsize
  //
  // "Specifies the minimum size of the operator when stretchy"
  //
  // values: length
  // default: set by dictionary (1em)
  //
  // We don't allow negative values.
  // Note: Contrary to other "length" values, unitless and percentage do not
  // give a multiple of the defaut value but a multiple of the operator at
  // normal size.
  //
  mMinSize = 0;
  GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::minsize_,
               value);
  if (!value.IsEmpty()) {
    nsCSSValue cssValue;
    if (nsMathMLElement::ParseNumericValue(value, cssValue,
                                           nsMathMLElement::
                                           PARSE_ALLOW_UNITLESS,
                                           mContent->OwnerDoc())) {
      nsCSSUnit unit = cssValue.GetUnit();
      if (eCSSUnit_Number == unit)
        mMinSize = cssValue.GetFloatValue();
      else if (eCSSUnit_Percent == unit)
        mMinSize = cssValue.GetPercentValue();
      else if (eCSSUnit_Null != unit) {
        mMinSize = float(CalcLength(presContext, mStyleContext, cssValue));
        mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
      }
    }
  }

  // maxsize
  //
  // "Specifies the maximum size of the operator when stretchy"
  //
  // values: length | "infinity"
  // default: set by dictionary (infinity)
  //
  // We don't allow negative values.
  // Note: Contrary to other "length" values, unitless and percentage do not
  // give a multiple of the defaut value but a multiple of the operator at
  // normal size.
  //
  mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
  GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::maxsize_,
               value);
  if (!value.IsEmpty()) {
    nsCSSValue cssValue;
    if (nsMathMLElement::ParseNumericValue(value, cssValue,
                                           nsMathMLElement::
                                           PARSE_ALLOW_UNITLESS,
                                           mContent->OwnerDoc())) {
      nsCSSUnit unit = cssValue.GetUnit();
      if (eCSSUnit_Number == unit)
        mMaxSize = cssValue.GetFloatValue();
      else if (eCSSUnit_Percent == unit)
        mMaxSize = cssValue.GetPercentValue();
      else if (eCSSUnit_Null != unit) {
        mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue));
        mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
      }
    }
  }
}
PRBool
nsMathMLOperators::LookupOperator(const nsString&       aOperator,
                                  const nsOperatorFlags aForm,
                                  nsOperatorFlags*      aFlags,
                                  float*                aLeftSpace,
                                  float*                aRightSpace)
{
  if (!gInitialized) {
    InitGlobals();
  }
  if (gOperatorTable) {
    NS_ASSERTION(aFlags && aLeftSpace && aRightSpace, "bad usage");
    NS_ASSERTION(aForm>=0 && aForm<4, "*** invalid call ***");

    OperatorData* found;
    PRInt32 form = NS_MATHML_OPERATOR_GET_FORM(aForm);
    gOperatorFound[NS_MATHML_OPERATOR_FORM_INFIX] = nsnull;
    gOperatorFound[NS_MATHML_OPERATOR_FORM_POSTFIX] = nsnull;
    gOperatorFound[NS_MATHML_OPERATOR_FORM_PREFIX] = nsnull;

    nsAutoString key(aOperator);
    key.AppendInt(form, 10);
    nsStringKey hkey(key);
    gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hkey);

    // If not found, check if the operator exists perhaps in a different form,
    // in the order of preference: infix, postfix, prefix
    if (!found) {
      if (form != NS_MATHML_OPERATOR_FORM_INFIX) {
        form = NS_MATHML_OPERATOR_FORM_INFIX;
        key.Assign(aOperator);
        key.AppendInt(form, 10);
        nsStringKey hashkey(key);
        gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
      }
      if (!found) {
        if (form != NS_MATHML_OPERATOR_FORM_POSTFIX) {
          form = NS_MATHML_OPERATOR_FORM_POSTFIX;
          key.Assign(aOperator);
          key.AppendInt(form, 10);
          nsStringKey hashkey(key);
          gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
        }
        if (!found) {
          if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
            form = NS_MATHML_OPERATOR_FORM_PREFIX;
            key.Assign(aOperator);
            key.AppendInt(form, 10);
            nsStringKey hashkey(key);
            gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
          }
        }
      }
    }
    if (found) {
      NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
      *aLeftSpace = found->mLeftSpace;
      *aRightSpace = found->mRightSpace;
      *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits
      *aFlags |= found->mFlags; // just add bits without overwriting
      return PR_TRUE;
    }
  }
  return PR_FALSE;
}