void nsMathMLmspaceFrame::ProcessAttributes(nsPresContext* aPresContext) { nsAutoString value; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); // width // // "Specifies the desired width of the space." // // values: length // default: 0em // // The default value is "0em", so unitless values can be ignored. // <mspace/> is listed among MathML elements allowing negative spacing and // the MathML test suite contains "Presentation/TokenElements/mspace/mspace2" // as an example. Hence we allow negative values. // mWidth = 0; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value); if (!value.IsEmpty()) { ParseNumericValue(value, &mWidth, nsMathMLElement::PARSE_ALLOW_NEGATIVE, aPresContext, mStyleContext, fontSizeInflation); } // height // // "Specifies the desired height (above the baseline) of the space." // // values: length // default: 0ex // // The default value is "0ex", so unitless values can be ignored. // We do not allow negative values. See bug 716349. // mHeight = 0; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value); if (!value.IsEmpty()) { ParseNumericValue(value, &mHeight, 0, aPresContext, mStyleContext, fontSizeInflation); } // depth // // "Specifies the desired depth (below the baseline) of the space." // // values: length // default: 0ex // // The default value is "0ex", so unitless values can be ignored. // We do not allow negative values. See bug 716349. // mDepth = 0; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::depth_, value); if (!value.IsEmpty()) { ParseNumericValue(value, &mDepth, 0, aPresContext, mStyleContext, fontSizeInflation); } }
void nsMathMLmmultiscriptsFrame::ProcessAttributes() { mSubScriptShift = 0; mSupScriptShift = 0; // check if the subscriptshift attribute is there nsAutoString value; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::subscriptshift_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) { mSubScriptShift = CalcLength(PresContext(), mStyleContext, cssValue); } } // check if the superscriptshift attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::superscriptshift_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) { mSupScriptShift = CalcLength(PresContext(), mStyleContext, cssValue); } } }
/* virtual */ nsresult nsMathMLmsubsupFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { // extra spacing between base and sup/subscript nscoord scriptSpace = 0; // subscriptshift // // "Specifies the minimum amount to shift the baseline of subscript down; the // default is for the rendering agent to use its own positioning rules." // // values: length // default: automatic // // We use 0 as the default value so unitless values can be ignored. // XXXfredw Should we forbid negative values? (bug 411227) // nsAutoString value; nscoord subScriptShift = 0; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::subscriptshift_, value); if (!value.IsEmpty()) { ParseNumericValue(value, &subScriptShift, nsMathMLElement::PARSE_ALLOW_NEGATIVE, PresContext(), mStyleContext); } // superscriptshift // // "Specifies the minimum amount to shift the baseline of superscript up; the // default is for the rendering agent to use its own positioning rules." // // values: length // default: automatic // // We use 0 as the default value so unitless values can be ignored. // XXXfredw Should we forbid negative values? (bug 411227) // nscoord supScriptShift = 0; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::superscriptshift_, value); if (!value.IsEmpty()) { ParseNumericValue(value, &supScriptShift, nsMathMLElement::PARSE_ALLOW_NEGATIVE, PresContext(), mStyleContext); } return nsMathMLmsubsupFrame::PlaceSubSupScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, subScriptShift, supScriptShift, scriptSpace); }
/* virtual */ nsresult nsMathMLmsubFrame::Place (nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { // extra spacing after sup/subscript nscoord scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f); // 0.5pt as in plain TeX // subscriptshift // // "Specifies the minimum amount to shift the baseline of subscript down; the // default is for the rendering agent to use its own positioning rules." // // values: length // default: automatic // // We use 0 as the default value so unitless values can be ignored. // As a minimum, negative values can be ignored. // nscoord subScriptShift = 0; nsAutoString value; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::subscriptshift_, value); if (!value.IsEmpty()) { ParseNumericValue(value, &subScriptShift, 0, PresContext(), mStyleContext); } return nsMathMLmsubFrame::PlaceSubScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, subScriptShift, scriptSpace); }
/* virtual */ nsresult nsMathMLmsupFrame::Place(nsRenderingContext& aRenderingContext, PRBool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { // extra spacing after sup/subscript nscoord scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f); // 0.5pt as in plain TeX // check if the superscriptshift attribute is there nsAutoString value; nscoord supScriptShift = 0; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::superscriptshift_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) && cssValue.IsLengthUnit()) { supScriptShift = CalcLength(PresContext(), mStyleContext, cssValue); } } return nsMathMLmsupFrame::PlaceSuperScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, supScriptShift, scriptSpace); }
void nsMathMLmspaceFrame::ProcessAttributes(nsPresContext* aPresContext) { /* parse the attributes width = number h-unit height = number v-unit depth = number v-unit */ nsAutoString value; nsCSSValue cssValue; // width mWidth = 0; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, mPresentationData.mstyle, nsMathMLAtoms::width_, value)) { if ((ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) && cssValue.IsLengthUnit()) { mWidth = CalcLength(aPresContext, mStyleContext, cssValue); } } // height mHeight = 0; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, mPresentationData.mstyle, nsMathMLAtoms::height_, value)) { if ((ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) && cssValue.IsLengthUnit()) { mHeight = CalcLength(aPresContext, mStyleContext, cssValue); } } // depth mDepth = 0; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, mPresentationData.mstyle, nsMathMLAtoms::depth_, value)) { if ((ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) && cssValue.IsLengthUnit()) { mDepth = CalcLength(aPresContext, mStyleContext, cssValue); } } }
void nsMathMLmmultiscriptsFrame::ProcessAttributes() { mSubScriptShift = 0; mSupScriptShift = 0; // subscriptshift // // "Specifies the minimum amount to shift the baseline of subscript down; the // default is for the rendering agent to use its own positioning rules." // // values: length // default: automatic // // We use 0 as the default value so unitless values can be ignored. // XXXfredw Should we forbid negative values? (bug 411227) // nsAutoString value; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::subscriptshift_, value); if (!value.IsEmpty()) { ParseNumericValue(value, &mSubScriptShift, nsMathMLElement::PARSE_ALLOW_NEGATIVE, PresContext(), mStyleContext); } // superscriptshift // // "Specifies the minimum amount to shift the baseline of superscript up; the // default is for the rendering agent to use its own positioning rules." // // values: length // default: automatic // // We use 0 as the default value so unitless values can be ignored. // XXXfredw Should we forbid negative values? (bug 411227) // GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::superscriptshift_, value); if (!value.IsEmpty()) { ParseNumericValue(value, &mSupScriptShift, nsMathMLElement::PARSE_ALLOW_NEGATIVE, PresContext(), mStyleContext); } }
nscoord nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext, nsStyleContext* aStyleContext, nsString& aThicknessAttribute, nscoord onePixel, nscoord aDefaultRuleThickness) { nscoord defaultThickness = aDefaultRuleThickness; nscoord lineThickness = aDefaultRuleThickness; nscoord minimumThickness = onePixel; // linethickness // // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The // default value is 'medium', 'thin' is thinner, but visible, 'thick' is // thicker; the exact thickness of these is left up to the rendering agent." // // values: length | "thin" | "medium" | "thick" // default: medium // if (!aThicknessAttribute.IsEmpty()) { if (aThicknessAttribute.EqualsLiteral("thin")) { lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE); minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS; // should visually decrease by at least one pixel, if default is not a pixel if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel) lineThickness = defaultThickness - onePixel; } else if (aThicknessAttribute.EqualsLiteral("medium")) { // medium is default } else if (aThicknessAttribute.EqualsLiteral("thick")) { lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE); minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS; // should visually increase by at least one pixel if (lineThickness < defaultThickness + onePixel) lineThickness = defaultThickness + onePixel; } else { // length value lineThickness = defaultThickness; ParseNumericValue(aThicknessAttribute, &lineThickness, nsMathMLElement::PARSE_ALLOW_UNITLESS, aPresContext, aStyleContext); } } // use minimum if the lineThickness is a non-zero value less than minimun if (lineThickness && lineThickness < minimumThickness) lineThickness = minimumThickness; return lineThickness; }
nscoord nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext, nsStyleContext* aStyleContext, nsString& aThicknessAttribute, nscoord onePixel, nscoord aDefaultRuleThickness) { nscoord defaultThickness = aDefaultRuleThickness; nscoord lineThickness = aDefaultRuleThickness; nscoord minimumThickness = onePixel; if (!aThicknessAttribute.IsEmpty()) { if (aThicknessAttribute.EqualsLiteral("thin")) { lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE); minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS; // should visually decrease by at least one pixel, if default is not a pixel if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel) lineThickness = defaultThickness - onePixel; } else if (aThicknessAttribute.EqualsLiteral("medium")) { lineThickness = NSToCoordRound(defaultThickness * MEDIUM_FRACTION_LINE); minimumThickness = onePixel * MEDIUM_FRACTION_LINE_MINIMUM_PIXELS; // should visually increase by at least one pixel if (lineThickness < defaultThickness + onePixel) lineThickness = defaultThickness + onePixel; } else if (aThicknessAttribute.EqualsLiteral("thick")) { lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE); minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS; // should visually increase by at least two pixels if (lineThickness < defaultThickness + 2*onePixel) lineThickness = defaultThickness + 2*onePixel; } else { // see if it is a plain number, or a percentage, or a h/v-unit like 1ex, 2px, 1em nsCSSValue cssValue; if (ParseNumericValue(aThicknessAttribute, cssValue)) { nsCSSUnit unit = cssValue.GetUnit(); if (eCSSUnit_Number == unit) lineThickness = nscoord(float(defaultThickness) * cssValue.GetFloatValue()); else if (eCSSUnit_Percent == unit) lineThickness = nscoord(float(defaultThickness) * cssValue.GetPercentValue()); else if (eCSSUnit_Null != unit) lineThickness = CalcLength(aPresContext, aStyleContext, cssValue); } } } // use minimum if the lineThickness is a non-zero value less than minimun if (lineThickness && lineThickness < minimumThickness) lineThickness = minimumThickness; return lineThickness; }
void nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, nsRuleData* aData) { if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) { const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_); if (value && value->Type() == nsAttrValue::eString && aData->mFontData->mScriptSizeMultiplier.GetUnit() == eCSSUnit_Null) { nsAutoString str(value->GetStringValue()); str.CompressWhitespace(); // MathML numbers can't have leading '+' if (str.Length() > 0 && str.CharAt(0) != '+') { PRInt32 errorCode; float floatValue = str.ToFloat(&errorCode); // Negative scriptsizemultipliers are not parsed if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) { aData->mFontData->mScriptSizeMultiplier. SetFloatValue(floatValue, eCSSUnit_Number); } } } value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_); if (value && value->Type() == nsAttrValue::eString && aData->mFontData->mScriptMinSize.GetUnit() == eCSSUnit_Null) { ParseNumericValue(value->GetStringValue(), aData->mFontData->mScriptMinSize, 0); } value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_); if (value && value->Type() == nsAttrValue::eString && aData->mFontData->mScriptLevel.GetUnit() == eCSSUnit_Null) { nsAutoString str(value->GetStringValue()); str.CompressWhitespace(); if (str.Length() > 0) { PRInt32 errorCode; PRInt32 intValue = str.ToInteger(&errorCode); if (NS_SUCCEEDED(errorCode)) { // This is kind of cheesy ... if the scriptlevel has a sign, // then it's a relative value and we store the nsCSSValue as an // Integer to indicate that. Otherwise we store it as a Number // to indicate that the scriptlevel is absolute. PRUnichar ch = str.CharAt(0); if (ch == '+' || ch == '-') { aData->mFontData->mScriptLevel.SetIntValue(intValue, eCSSUnit_Integer); } else { aData->mFontData->mScriptLevel.SetFloatValue(intValue, eCSSUnit_Number); } } } } PRBool parseSizeKeywords = PR_TRUE; value = aAttributes->GetAttr(nsGkAtoms::mathsize_); if (!value) { parseSizeKeywords = PR_FALSE; value = aAttributes->GetAttr(nsGkAtoms::fontsize_); } if (value && value->Type() == nsAttrValue::eString && aData->mFontData->mSize.GetUnit() == eCSSUnit_Null) { nsAutoString str(value->GetStringValue()); if (!ParseNumericValue(str, aData->mFontData->mSize, 0) && parseSizeKeywords) { static const char sizes[3][7] = { "small", "normal", "big" }; static const PRInt32 values[NS_ARRAY_LENGTH(sizes)] = { NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM, NS_STYLE_FONT_SIZE_LARGE }; str.CompressWhitespace(); for (PRInt32 i = 0; i < NS_ARRAY_LENGTH(sizes); ++i) { if (str.EqualsASCII(sizes[i])) { aData->mFontData->mSize.SetIntValue(values[i], eCSSUnit_Enumerated); break; } } } } value = aAttributes->GetAttr(nsGkAtoms::fontfamily_); if (value && value->Type() == nsAttrValue::eString && aData->mFontData->mFamily.GetUnit() == eCSSUnit_Null) { aData->mFontData->mFamily.SetStringValue(value->GetStringValue(), eCSSUnit_String); aData->mFontData->mFamilyFromHTML = PR_FALSE; } } if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)) { const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::mathbackground_); if (!value) { value = aAttributes->GetAttr(nsGkAtoms::background); } if (value && aData->mColorData->mBackColor.GetUnit() == eCSSUnit_Null) { nscolor color; if (value->GetColorValue(color)) { aData->mColorData->mBackColor.SetColorValue(color); } } } if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) { const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::mathcolor_); if (!value) { value = aAttributes->GetAttr(nsGkAtoms::color); } nscolor color; if (value && value->GetColorValue(color) && aData->mColorData->mColor.GetUnit() == eCSSUnit_Null) { aData->mColorData->mColor.SetColorValue(color); } } }
// parse an input string in the following format (see bug 148326 for testcases): // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace) PRBool nsMathMLmpaddedFrame::ParseAttribute(nsString& aString, PRInt32& aSign, nsCSSValue& aCSSValue, PRInt32& aPseudoUnit) { aCSSValue.Reset(); aSign = NS_MATHML_SIGN_INVALID; aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED; aString.CompressWhitespace(); // aString is not a const in this code PRInt32 stringLength = aString.Length(); if (!stringLength) return PR_FALSE; nsAutoString number, unit; ////////////////////// // see if the sign is there PRInt32 i = 0; if (aString[0] == '+') { aSign = NS_MATHML_SIGN_PLUS; i++; } else if (aString[0] == '-') { aSign = NS_MATHML_SIGN_MINUS; i++; } else aSign = NS_MATHML_SIGN_UNSPECIFIED; // skip any space after the sign if (i < stringLength && nsCRT::IsAsciiSpace(aString[i])) i++; // get the number PRBool gotDot = PR_FALSE, gotPercent = PR_FALSE; for (; i < stringLength; i++) { PRUnichar c = aString[i]; if (gotDot && c == '.') { // error - two dots encountered aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } if (c == '.') gotDot = PR_TRUE; else if (!nsCRT::IsAsciiDigit(c)) { break; } number.Append(c); } // catch error if we didn't enter the loop above... we could simply initialize // floatValue = 1, to cater for cases such as width="height", but that wouldn't // be in line with the spec which requires an explicit number if (number.IsEmpty()) { #ifdef NS_DEBUG printf("mpadded: attribute with bad numeric value: %s\n", NS_LossyConvertUTF16toASCII(aString).get()); #endif aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } PRInt32 errorCode; float floatValue = number.ToFloat(&errorCode); if (errorCode) { aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } // skip any space after the number if (i < stringLength && nsCRT::IsAsciiSpace(aString[i])) i++; // see if this is a percentage-based value if (i < stringLength && aString[i] == '%') { i++; gotPercent = PR_TRUE; // skip any space after the '%' sign if (i < stringLength && nsCRT::IsAsciiSpace(aString[i])) i++; } // the remainder now should be a css-unit, or a pseudo-unit, or a named-space aString.Right(unit, stringLength - i); if (unit.IsEmpty()) { // also cater for the edge case of "0" for which the unit is optional if (gotPercent || !floatValue) { aCSSValue.SetPercentValue(floatValue / 100.0f); aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF; return PR_TRUE; } /* else { // no explicit CSS unit and no explicit pseudo-unit... // In this case, the MathML REC suggests taking ems for // h-unit (width, lspace) or exs for v-unit (height, depth). // Here, however, we explicitly request authors to specify // the unit. This is more in line with the CSS REC (and // it allows keeping the code simpler...) } */ } else if (unit.EqualsLiteral("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH; else if (unit.EqualsLiteral("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT; else if (unit.EqualsLiteral("depth")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH; else if (unit.EqualsLiteral("lspace")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_LSPACE; else if (!gotPercent) { // percentage can only apply to a pseudo-unit // see if the unit is a named-space // XXX nsnull in ParseNamedSpacedValue()? don't access mstyle? if (ParseNamedSpaceValue(nsnull, unit, aCSSValue)) { // re-scale properly, and we know that the unit of the named-space is 'em' floatValue *= aCSSValue.GetFloatValue(); aCSSValue.SetFloatValue(floatValue, eCSSUnit_EM); aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE; return PR_TRUE; } // see if the input was just a CSS value number.Append(unit); // leave the sign out if it was there if (ParseNumericValue(number, aCSSValue)) return PR_TRUE; } // if we enter here, we have a number that will act as a multiplier on a pseudo-unit if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) { if (gotPercent) aCSSValue.SetPercentValue(floatValue / 100.0f); else aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number); return PR_TRUE; } #ifdef NS_DEBUG printf("mpadded: attribute with bad numeric value: %s\n", NS_LossyConvertUTF16toASCII(aString).get()); #endif // if we reach here, it means we encounter an unexpected input aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; }
/* static */ PRInt32 nsMathMLFrame::MapAttributesIntoCSS(nsPresContext* aPresContext, nsIContent* aContent) { // normal case, quick return if there are no attributes NS_ASSERTION(aContent, "null arg"); PRUint32 attrCount = 0; if (aContent) attrCount = aContent->GetAttrCount(); if (!attrCount) return 0; // need to initialize here -- i.e., after registering nsMathMLAtoms static const nsCSSMapping kCSSMappingTable[] = { {kMathMLversion2, nsMathMLAtoms::mathcolor_, "color:"}, {kMathMLversion1, nsMathMLAtoms::color_, "color:"}, {kMathMLversion2, nsMathMLAtoms::mathsize_, "font-size:"}, {kMathMLversion1, nsMathMLAtoms::fontsize_, "font-size:"}, {kMathMLversion1, nsMathMLAtoms::fontfamily_, "font-family:"}, {kMathMLversion2, nsMathMLAtoms::mathbackground_, "background-color:"}, {kMathMLversion1, nsMathMLAtoms::background_, "background-color:"}, {0, nsnull, nsnull} }; nsCOMPtr<nsIDocument> doc; nsCOMPtr<nsIStyleSheet> sheet; nsCOMPtr<nsICSSStyleSheet> cssSheet; nsCOMPtr<nsIDOMCSSStyleSheet> domSheet; PRInt32 nameSpaceID; nsCOMPtr<nsIAtom> prefix; nsCOMPtr<nsIAtom> attrAtom; PRInt32 ruleCount = 0; for (PRUint32 i = 0; i < attrCount; ++i) { aContent->GetAttrNameAt(i, &nameSpaceID, getter_AddRefs(attrAtom), getter_AddRefs(prefix)); // lookup the equivalent CSS property const nsCSSMapping* map = kCSSMappingTable; while (map->attrAtom && map->attrAtom != attrAtom) ++map; if (!map->attrAtom) continue; nsAutoString cssProperty(NS_ConvertASCIItoUCS2(map->cssProperty)); nsAutoString attrValue; aContent->GetAttr(nameSpaceID, attrAtom, attrValue); if (attrValue.IsEmpty()) continue; nsAutoString escapedAttrValue; nsStyleUtil::EscapeCSSString(attrValue, escapedAttrValue); // don't add rules that are already in mathml.css // (this will also clean up whitespace before units - see bug 125303) if (attrAtom == nsMathMLAtoms::fontsize_ || attrAtom == nsMathMLAtoms::mathsize_) { nsCSSValue cssValue; nsAutoString numericValue(attrValue); if (!ParseNumericValue(numericValue, cssValue)) continue; // on exit, ParseNumericValue also returns a nicer string // in which the whitespace before the unit is cleaned up cssProperty.Append(numericValue); } else cssProperty.Append(attrValue); nsAutoString attrName; attrAtom->ToString(attrName); // make a style rule that maps to the equivalent CSS property nsAutoString cssRule; cssRule.Assign(NS_LITERAL_STRING("[") + attrName + NS_LITERAL_STRING("='") + escapedAttrValue + NS_LITERAL_STRING("']{") + cssProperty + NS_LITERAL_STRING("}")); if (!sheet) { // first time... we do this to defer the lookup up to the // point where we encounter attributes that actually matter doc = aContent->GetDocument(); if (!doc) return 0; GetMathMLAttributeStyleSheet(aPresContext, getter_AddRefs(sheet)); if (!sheet) return 0; // by construction, these cannot be null at this point cssSheet = do_QueryInterface(sheet); domSheet = do_QueryInterface(sheet); NS_ASSERTION(cssSheet && domSheet, "unexpected null pointers"); // we will keep the sheet orphan as we populate it. This way, // observers of the document won't be notified and we avoid any troubles // that may come from reconstructing the frame tree. Our rules only need // a re-resolve of style data and a reflow, not a reconstruct-all... sheet->SetOwningDocument(nsnull); } // check for duplicate, if a similar rule is already there, don't bother to add another one nsAutoString selector; selector.Assign(NS_LITERAL_STRING("*[") + attrName + NS_LITERAL_STRING("=\"") + escapedAttrValue + NS_LITERAL_STRING("\"]")); PRInt32 k, count; cssSheet->StyleRuleCount(count); for (k = 0; k < count; ++k) { nsAutoString tmpSelector; nsCOMPtr<nsICSSRule> tmpRule; cssSheet->GetStyleRuleAt(k, *getter_AddRefs(tmpRule)); nsCOMPtr<nsICSSStyleRule> tmpStyleRule = do_QueryInterface(tmpRule); if (tmpStyleRule) { tmpStyleRule->GetSelectorText(tmpSelector); if (tmpSelector.Equals(selector)) { k = -1; break; } } } if (k >= 0) { // insert the rule (note: when the sheet already has @namespace and // friends, insert after them, e.g., at the end, otherwise it won't work) // For MathML 2, insert at the end to give it precedence PRInt32 pos = (map->compatibility == kMathMLversion2) ? count : 1; PRUint32 index; domSheet->InsertRule(cssRule, pos, &index); ++ruleCount; } } // restore the sheet to its owner if (sheet) { sheet->SetOwningDocument(doc); } return ruleCount; }
/* static */ PRBool nsMathMLFrame::ParseNamedSpaceValue(nsIFrame* aMathMLmstyleFrame, nsString& aString, nsCSSValue& aCSSValue) { aCSSValue.Reset(); aString.CompressWhitespace(); // aString is not a const in this code... if (!aString.Length()) return PR_FALSE; // See if it is one of the 'namedspace' (ranging 1/18em...7/18em) PRInt32 i = 0; nsIAtom* namedspaceAtom = nsnull; if (aString.EqualsLiteral("veryverythinmathspace")) { i = 1; namedspaceAtom = nsMathMLAtoms::veryverythinmathspace_; } else if (aString.EqualsLiteral("verythinmathspace")) { i = 2; namedspaceAtom = nsMathMLAtoms::verythinmathspace_; } else if (aString.EqualsLiteral("thinmathspace")) { i = 3; namedspaceAtom = nsMathMLAtoms::thinmathspace_; } else if (aString.EqualsLiteral("mediummathspace")) { i = 4; namedspaceAtom = nsMathMLAtoms::mediummathspace_; } else if (aString.EqualsLiteral("thickmathspace")) { i = 5; namedspaceAtom = nsMathMLAtoms::thickmathspace_; } else if (aString.EqualsLiteral("verythickmathspace")) { i = 6; namedspaceAtom = nsMathMLAtoms::verythickmathspace_; } else if (aString.EqualsLiteral("veryverythickmathspace")) { i = 7; namedspaceAtom = nsMathMLAtoms::veryverythickmathspace_; } if (0 != i) { if (aMathMLmstyleFrame) { // see if there is a <mstyle> that has overriden the default value // GetAttribute() will recurse all the way up into the <mstyle> hierarchy nsAutoString value; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(nsnull, aMathMLmstyleFrame, namedspaceAtom, value)) { if (ParseNumericValue(value, aCSSValue) && aCSSValue.IsLengthUnit()) { return PR_TRUE; } } } // fall back to the default value aCSSValue.SetFloatValue(float(i)/float(18), eCSSUnit_EM); return PR_TRUE; } return PR_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 = nsnull; mEmbellishData.leftSpace = 0; mEmbellishData.rightSpace = 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; // lookup the operator dictionary float lspace = 0.0f; float rspace = 0.0f; nsAutoString data; mMathMLChar.GetData(data); PRBool found = nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace); if (found && (lspace || rspace)) { // cache the default values of lspace & rspace that we get from the dictionary. // 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.leftSpace = NSToCoordRound(lspace * em); mEmbellishData.rightSpace = 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 (GetStyleFont()->mScriptLevel > 0) { if (NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags)) { // could be an isolated accent or script, e.g., x^{+}, just zero out mEmbellishData.leftSpace = 0; mEmbellishData.rightSpace = 0; } else if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { mEmbellishData.leftSpace /= 2; mEmbellishData.rightSpace /= 2; } } } } // If we are an accent without explicit lspace="." or rspace=".", // we will ignore our default left/right space // lspace = number h-unit | namedspace nscoord leftSpace = mEmbellishData.leftSpace; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::lspace_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) { if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue()) leftSpace = 0; else if (cssValue.IsLengthUnit()) leftSpace = CalcLength(presContext, mStyleContext, cssValue); mFlags |= NS_MATHML_OPERATOR_LEFTSPACE_ATTR; } } // rspace = number h-unit | namedspace nscoord rightSpace = mEmbellishData.rightSpace; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::rspace_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) { if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue()) rightSpace = 0; else if (cssValue.IsLengthUnit()) rightSpace = CalcLength(presContext, mStyleContext, cssValue); mFlags |= NS_MATHML_OPERATOR_RIGHTSPACE_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 (leftSpace || rightSpace) { nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); if (leftSpace && leftSpace < onePixel) leftSpace = onePixel; if (rightSpace && rightSpace < onePixel) rightSpace = onePixel; } // the values that we get from our attributes override the dictionary mEmbellishData.leftSpace = leftSpace; mEmbellishData.rightSpace = rightSpace; // 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 = number [ v-unit | h-unit ] | namedspace mMinSize = 0.0; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::minsize_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) { 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; } if ((eCSSUnit_Number == unit) || (eCSSUnit_Percent == unit)) { // see if the multiplicative inheritance should be from <mstyle> GetAttribute(nsnull, mPresentationData.mstyle, nsGkAtoms::minsize_, value); if (!value.IsEmpty()) { if (ParseNumericValue(value, cssValue)) { if (cssValue.IsLengthUnit()) { mMinSize *= float(CalcLength(presContext, mStyleContext, cssValue)); mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE; } } } } } } // maxsize = number [ v-unit | h-unit ] | namedspace | infinity mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::maxsize_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (ParseNumericValue(value, cssValue) || ParseNamedSpaceValue(mPresentationData.mstyle, value, cssValue)) { 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; } if ((eCSSUnit_Number == unit) || (eCSSUnit_Percent == unit)) { // see if the multiplicative inheritance should be from <mstyle> GetAttribute(nsnull, mPresentationData.mstyle, nsGkAtoms::maxsize_, value); if (!value.IsEmpty()) { if (ParseNumericValue(value, cssValue)) { if (cssValue.IsLengthUnit()) { mMaxSize *= float(CalcLength(presContext, mStyleContext, cssValue)); mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE; } } } } } } }