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; }