Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsMathMLFrame.h" |
michael@0 | 7 | #include "nsNameSpaceManager.h" |
michael@0 | 8 | #include "nsMathMLChar.h" |
michael@0 | 9 | #include "nsCSSPseudoElements.h" |
michael@0 | 10 | #include "nsMathMLElement.h" |
michael@0 | 11 | |
michael@0 | 12 | // used to map attributes into CSS rules |
michael@0 | 13 | #include "nsStyleSet.h" |
michael@0 | 14 | #include "nsAutoPtr.h" |
michael@0 | 15 | #include "nsDisplayList.h" |
michael@0 | 16 | #include "nsRenderingContext.h" |
michael@0 | 17 | |
michael@0 | 18 | eMathMLFrameType |
michael@0 | 19 | nsMathMLFrame::GetMathMLFrameType() |
michael@0 | 20 | { |
michael@0 | 21 | // see if it is an embellished operator (mapped to 'Op' in TeX) |
michael@0 | 22 | if (mEmbellishData.coreFrame) |
michael@0 | 23 | return GetMathMLFrameTypeFor(mEmbellishData.coreFrame); |
michael@0 | 24 | |
michael@0 | 25 | // if it has a prescribed base, fetch the type from there |
michael@0 | 26 | if (mPresentationData.baseFrame) |
michael@0 | 27 | return GetMathMLFrameTypeFor(mPresentationData.baseFrame); |
michael@0 | 28 | |
michael@0 | 29 | // everything else is treated as ordinary (mapped to 'Ord' in TeX) |
michael@0 | 30 | return eMathMLFrameType_Ordinary; |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | NS_IMETHODIMP |
michael@0 | 34 | nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent) |
michael@0 | 35 | { |
michael@0 | 36 | mEmbellishData.flags = 0; |
michael@0 | 37 | mEmbellishData.coreFrame = nullptr; |
michael@0 | 38 | mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; |
michael@0 | 39 | mEmbellishData.leadingSpace = 0; |
michael@0 | 40 | mEmbellishData.trailingSpace = 0; |
michael@0 | 41 | |
michael@0 | 42 | mPresentationData.flags = 0; |
michael@0 | 43 | mPresentationData.baseFrame = nullptr; |
michael@0 | 44 | |
michael@0 | 45 | // by default, just inherit the display of our parent |
michael@0 | 46 | nsPresentationData parentData; |
michael@0 | 47 | GetPresentationDataFrom(aParent, parentData); |
michael@0 | 48 | |
michael@0 | 49 | #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) |
michael@0 | 50 | mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS; |
michael@0 | 51 | #endif |
michael@0 | 52 | |
michael@0 | 53 | return NS_OK; |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | NS_IMETHODIMP |
michael@0 | 57 | nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues, |
michael@0 | 58 | uint32_t aWhichFlags) |
michael@0 | 59 | { |
michael@0 | 60 | NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags), |
michael@0 | 61 | "aWhichFlags should only be compression flag"); |
michael@0 | 62 | |
michael@0 | 63 | if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) { |
michael@0 | 64 | // updating the compression flag is allowed |
michael@0 | 65 | if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) { |
michael@0 | 66 | // 'compressed' means 'prime' style in App. G, TeXbook |
michael@0 | 67 | mPresentationData.flags |= NS_MATHML_COMPRESSED; |
michael@0 | 68 | } |
michael@0 | 69 | // no else. the flag is sticky. it retains its value once it is set |
michael@0 | 70 | } |
michael@0 | 71 | return NS_OK; |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | // Helper to give a style context suitable for doing the stretching of |
michael@0 | 75 | // a MathMLChar. Frame classes that use this should ensure that the |
michael@0 | 76 | // extra leaf style contexts given to the MathMLChars are accessible to |
michael@0 | 77 | // the Style System via the Get/Set AdditionalStyleContext() APIs. |
michael@0 | 78 | /* static */ void |
michael@0 | 79 | nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext, |
michael@0 | 80 | nsIContent* aContent, |
michael@0 | 81 | nsStyleContext* aParentStyleContext, |
michael@0 | 82 | nsMathMLChar* aMathMLChar) |
michael@0 | 83 | { |
michael@0 | 84 | nsCSSPseudoElements::Type pseudoType = |
michael@0 | 85 | nsCSSPseudoElements::ePseudo_mozMathAnonymous; // savings |
michael@0 | 86 | nsRefPtr<nsStyleContext> newStyleContext; |
michael@0 | 87 | newStyleContext = aPresContext->StyleSet()-> |
michael@0 | 88 | ResolvePseudoElementStyle(aContent->AsElement(), pseudoType, |
michael@0 | 89 | aParentStyleContext, nullptr); |
michael@0 | 90 | |
michael@0 | 91 | aMathMLChar->SetStyleContext(newStyleContext); |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | /* static */ void |
michael@0 | 95 | nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame, |
michael@0 | 96 | nsEmbellishData& aEmbellishData) |
michael@0 | 97 | { |
michael@0 | 98 | // initialize OUT params |
michael@0 | 99 | aEmbellishData.flags = 0; |
michael@0 | 100 | aEmbellishData.coreFrame = nullptr; |
michael@0 | 101 | aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; |
michael@0 | 102 | aEmbellishData.leadingSpace = 0; |
michael@0 | 103 | aEmbellishData.trailingSpace = 0; |
michael@0 | 104 | |
michael@0 | 105 | if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) { |
michael@0 | 106 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame); |
michael@0 | 107 | if (mathMLFrame) { |
michael@0 | 108 | mathMLFrame->GetEmbellishData(aEmbellishData); |
michael@0 | 109 | } |
michael@0 | 110 | } |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | // helper to get the presentation data of a frame, by possibly walking up |
michael@0 | 114 | // the frame hierarchy if we happen to be surrounded by non-MathML frames. |
michael@0 | 115 | /* static */ void |
michael@0 | 116 | nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame, |
michael@0 | 117 | nsPresentationData& aPresentationData, |
michael@0 | 118 | bool aClimbTree) |
michael@0 | 119 | { |
michael@0 | 120 | // initialize OUT params |
michael@0 | 121 | aPresentationData.flags = 0; |
michael@0 | 122 | aPresentationData.baseFrame = nullptr; |
michael@0 | 123 | |
michael@0 | 124 | nsIFrame* frame = aFrame; |
michael@0 | 125 | while (frame) { |
michael@0 | 126 | if (frame->IsFrameOfType(nsIFrame::eMathML)) { |
michael@0 | 127 | nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame); |
michael@0 | 128 | if (mathMLFrame) { |
michael@0 | 129 | mathMLFrame->GetPresentationData(aPresentationData); |
michael@0 | 130 | break; |
michael@0 | 131 | } |
michael@0 | 132 | } |
michael@0 | 133 | // stop if the caller doesn't want to lookup beyond the frame |
michael@0 | 134 | if (!aClimbTree) { |
michael@0 | 135 | break; |
michael@0 | 136 | } |
michael@0 | 137 | // stop if we reach the root <math> tag |
michael@0 | 138 | nsIContent* content = frame->GetContent(); |
michael@0 | 139 | NS_ASSERTION(content || !frame->GetParent(), // no assert for the root |
michael@0 | 140 | "dangling frame without a content node"); |
michael@0 | 141 | if (!content) |
michael@0 | 142 | break; |
michael@0 | 143 | |
michael@0 | 144 | if (content->Tag() == nsGkAtoms::math) { |
michael@0 | 145 | break; |
michael@0 | 146 | } |
michael@0 | 147 | frame = frame->GetParent(); |
michael@0 | 148 | } |
michael@0 | 149 | NS_WARN_IF_FALSE(frame && frame->GetContent(), |
michael@0 | 150 | "bad MathML markup - could not find the top <math> element"); |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | /* static */ void |
michael@0 | 154 | nsMathMLFrame::GetRuleThickness(nsRenderingContext& aRenderingContext, |
michael@0 | 155 | nsFontMetrics* aFontMetrics, |
michael@0 | 156 | nscoord& aRuleThickness) |
michael@0 | 157 | { |
michael@0 | 158 | // get the bounding metrics of the overbar char, the rendering context |
michael@0 | 159 | // is assumed to have been set with the font of the current style context |
michael@0 | 160 | NS_ASSERTION(aRenderingContext.FontMetrics()->Font(). |
michael@0 | 161 | Equals(aFontMetrics->Font()), |
michael@0 | 162 | "unexpected state"); |
michael@0 | 163 | |
michael@0 | 164 | nscoord xHeight = aFontMetrics->XHeight(); |
michael@0 | 165 | char16_t overBar = 0x00AF; |
michael@0 | 166 | nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&overBar, 1); |
michael@0 | 167 | aRuleThickness = bm.ascent + bm.descent; |
michael@0 | 168 | if (aRuleThickness <= 0 || aRuleThickness >= xHeight) { |
michael@0 | 169 | // fall-back to the other version |
michael@0 | 170 | GetRuleThickness(aFontMetrics, aRuleThickness); |
michael@0 | 171 | } |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | /* static */ void |
michael@0 | 175 | nsMathMLFrame::GetAxisHeight(nsRenderingContext& aRenderingContext, |
michael@0 | 176 | nsFontMetrics* aFontMetrics, |
michael@0 | 177 | nscoord& aAxisHeight) |
michael@0 | 178 | { |
michael@0 | 179 | // get the bounding metrics of the minus sign, the rendering context |
michael@0 | 180 | // is assumed to have been set with the font of the current style context |
michael@0 | 181 | NS_ASSERTION(aRenderingContext.FontMetrics()->Font(). |
michael@0 | 182 | Equals(aFontMetrics->Font()), |
michael@0 | 183 | "unexpected state"); |
michael@0 | 184 | |
michael@0 | 185 | nscoord xHeight = aFontMetrics->XHeight(); |
michael@0 | 186 | char16_t minus = 0x2212; // not '-', but official Unicode minus sign |
michael@0 | 187 | nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&minus, 1); |
michael@0 | 188 | aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2; |
michael@0 | 189 | if (aAxisHeight <= 0 || aAxisHeight >= xHeight) { |
michael@0 | 190 | // fall-back to the other version |
michael@0 | 191 | GetAxisHeight(aFontMetrics, aAxisHeight); |
michael@0 | 192 | } |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | /* static */ nscoord |
michael@0 | 196 | nsMathMLFrame::CalcLength(nsPresContext* aPresContext, |
michael@0 | 197 | nsStyleContext* aStyleContext, |
michael@0 | 198 | const nsCSSValue& aCSSValue) |
michael@0 | 199 | { |
michael@0 | 200 | NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit"); |
michael@0 | 201 | |
michael@0 | 202 | if (aCSSValue.IsFixedLengthUnit()) { |
michael@0 | 203 | return aCSSValue.GetFixedLength(aPresContext); |
michael@0 | 204 | } |
michael@0 | 205 | if (aCSSValue.IsPixelLengthUnit()) { |
michael@0 | 206 | return aCSSValue.GetPixelLength(); |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | nsCSSUnit unit = aCSSValue.GetUnit(); |
michael@0 | 210 | |
michael@0 | 211 | if (eCSSUnit_EM == unit) { |
michael@0 | 212 | const nsStyleFont* font = aStyleContext->StyleFont(); |
michael@0 | 213 | return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size); |
michael@0 | 214 | } |
michael@0 | 215 | else if (eCSSUnit_XHeight == unit) { |
michael@0 | 216 | nsRefPtr<nsFontMetrics> fm; |
michael@0 | 217 | nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext, |
michael@0 | 218 | getter_AddRefs(fm)); |
michael@0 | 219 | nscoord xHeight = fm->XHeight(); |
michael@0 | 220 | return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight); |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | // MathML doesn't specify other CSS units such as rem or ch |
michael@0 | 224 | NS_ERROR("Unsupported unit"); |
michael@0 | 225 | return 0; |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | /* static */ void |
michael@0 | 229 | nsMathMLFrame::ParseNumericValue(const nsString& aString, |
michael@0 | 230 | nscoord* aLengthValue, |
michael@0 | 231 | uint32_t aFlags, |
michael@0 | 232 | nsPresContext* aPresContext, |
michael@0 | 233 | nsStyleContext* aStyleContext) |
michael@0 | 234 | { |
michael@0 | 235 | nsCSSValue cssValue; |
michael@0 | 236 | |
michael@0 | 237 | if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags, |
michael@0 | 238 | aPresContext->Document())) { |
michael@0 | 239 | // Invalid attribute value. aLengthValue remains unchanged, so the default |
michael@0 | 240 | // length value is used. |
michael@0 | 241 | return; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | nsCSSUnit unit = cssValue.GetUnit(); |
michael@0 | 245 | |
michael@0 | 246 | if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) { |
michael@0 | 247 | // Relative units. A multiple of the default length value is used. |
michael@0 | 248 | *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ? |
michael@0 | 249 | cssValue.GetPercentValue() : |
michael@0 | 250 | cssValue.GetFloatValue())); |
michael@0 | 251 | return; |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | // Absolute units. |
michael@0 | 255 | *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue); |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | // ================ |
michael@0 | 259 | // Utils to map attributes into CSS rules (work-around to bug 69409 which |
michael@0 | 260 | // is not scheduled to be fixed anytime soon) |
michael@0 | 261 | // |
michael@0 | 262 | |
michael@0 | 263 | struct |
michael@0 | 264 | nsCSSMapping { |
michael@0 | 265 | int32_t compatibility; |
michael@0 | 266 | const nsIAtom* attrAtom; |
michael@0 | 267 | const char* cssProperty; |
michael@0 | 268 | }; |
michael@0 | 269 | |
michael@0 | 270 | #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) |
michael@0 | 271 | class nsDisplayMathMLBoundingMetrics : public nsDisplayItem { |
michael@0 | 272 | public: |
michael@0 | 273 | nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder, |
michael@0 | 274 | nsIFrame* aFrame, const nsRect& aRect) |
michael@0 | 275 | : nsDisplayItem(aBuilder, aFrame), mRect(aRect) { |
michael@0 | 276 | MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics); |
michael@0 | 277 | } |
michael@0 | 278 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 279 | virtual ~nsDisplayMathMLBoundingMetrics() { |
michael@0 | 280 | MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics); |
michael@0 | 281 | } |
michael@0 | 282 | #endif |
michael@0 | 283 | |
michael@0 | 284 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 285 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 286 | NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS) |
michael@0 | 287 | private: |
michael@0 | 288 | nsRect mRect; |
michael@0 | 289 | }; |
michael@0 | 290 | |
michael@0 | 291 | void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 292 | nsRenderingContext* aCtx) |
michael@0 | 293 | { |
michael@0 | 294 | aCtx->SetColor(NS_RGB(0,0,255)); |
michael@0 | 295 | aCtx->DrawRect(mRect + ToReferenceFrame()); |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | nsresult |
michael@0 | 299 | nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder, |
michael@0 | 300 | nsIFrame* aFrame, const nsPoint& aPt, |
michael@0 | 301 | const nsBoundingMetrics& aMetrics, |
michael@0 | 302 | const nsDisplayListSet& aLists) { |
michael@0 | 303 | if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags)) |
michael@0 | 304 | return NS_OK; |
michael@0 | 305 | |
michael@0 | 306 | nscoord x = aPt.x + aMetrics.leftBearing; |
michael@0 | 307 | nscoord y = aPt.y - aMetrics.ascent; |
michael@0 | 308 | nscoord w = aMetrics.rightBearing - aMetrics.leftBearing; |
michael@0 | 309 | nscoord h = aMetrics.ascent + aMetrics.descent; |
michael@0 | 310 | |
michael@0 | 311 | return aLists.Content()->AppendNewToTop(new (aBuilder) |
michael@0 | 312 | nsDisplayMathMLBoundingMetrics(aBuilder, this, nsRect(x,y,w,h))); |
michael@0 | 313 | } |
michael@0 | 314 | #endif |
michael@0 | 315 | |
michael@0 | 316 | class nsDisplayMathMLBar : public nsDisplayItem { |
michael@0 | 317 | public: |
michael@0 | 318 | nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder, |
michael@0 | 319 | nsIFrame* aFrame, const nsRect& aRect) |
michael@0 | 320 | : nsDisplayItem(aBuilder, aFrame), mRect(aRect) { |
michael@0 | 321 | MOZ_COUNT_CTOR(nsDisplayMathMLBar); |
michael@0 | 322 | } |
michael@0 | 323 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 324 | virtual ~nsDisplayMathMLBar() { |
michael@0 | 325 | MOZ_COUNT_DTOR(nsDisplayMathMLBar); |
michael@0 | 326 | } |
michael@0 | 327 | #endif |
michael@0 | 328 | |
michael@0 | 329 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 330 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 331 | NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR) |
michael@0 | 332 | private: |
michael@0 | 333 | nsRect mRect; |
michael@0 | 334 | }; |
michael@0 | 335 | |
michael@0 | 336 | void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 337 | nsRenderingContext* aCtx) |
michael@0 | 338 | { |
michael@0 | 339 | // paint the bar with the current text color |
michael@0 | 340 | aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); |
michael@0 | 341 | aCtx->FillRect(mRect + ToReferenceFrame()); |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | void |
michael@0 | 345 | nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder, |
michael@0 | 346 | nsIFrame* aFrame, const nsRect& aRect, |
michael@0 | 347 | const nsDisplayListSet& aLists) { |
michael@0 | 348 | if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) |
michael@0 | 349 | return; |
michael@0 | 350 | |
michael@0 | 351 | aLists.Content()->AppendNewToTop(new (aBuilder) |
michael@0 | 352 | nsDisplayMathMLBar(aBuilder, aFrame, aRect)); |
michael@0 | 353 | } |