1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/mathml/nsMathMLFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,353 @@ 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 "nsMathMLFrame.h" 1.10 +#include "nsNameSpaceManager.h" 1.11 +#include "nsMathMLChar.h" 1.12 +#include "nsCSSPseudoElements.h" 1.13 +#include "nsMathMLElement.h" 1.14 + 1.15 +// used to map attributes into CSS rules 1.16 +#include "nsStyleSet.h" 1.17 +#include "nsAutoPtr.h" 1.18 +#include "nsDisplayList.h" 1.19 +#include "nsRenderingContext.h" 1.20 + 1.21 +eMathMLFrameType 1.22 +nsMathMLFrame::GetMathMLFrameType() 1.23 +{ 1.24 + // see if it is an embellished operator (mapped to 'Op' in TeX) 1.25 + if (mEmbellishData.coreFrame) 1.26 + return GetMathMLFrameTypeFor(mEmbellishData.coreFrame); 1.27 + 1.28 + // if it has a prescribed base, fetch the type from there 1.29 + if (mPresentationData.baseFrame) 1.30 + return GetMathMLFrameTypeFor(mPresentationData.baseFrame); 1.31 + 1.32 + // everything else is treated as ordinary (mapped to 'Ord' in TeX) 1.33 + return eMathMLFrameType_Ordinary; 1.34 +} 1.35 + 1.36 +NS_IMETHODIMP 1.37 +nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent) 1.38 +{ 1.39 + mEmbellishData.flags = 0; 1.40 + mEmbellishData.coreFrame = nullptr; 1.41 + mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; 1.42 + mEmbellishData.leadingSpace = 0; 1.43 + mEmbellishData.trailingSpace = 0; 1.44 + 1.45 + mPresentationData.flags = 0; 1.46 + mPresentationData.baseFrame = nullptr; 1.47 + 1.48 + // by default, just inherit the display of our parent 1.49 + nsPresentationData parentData; 1.50 + GetPresentationDataFrom(aParent, parentData); 1.51 + 1.52 +#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) 1.53 + mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS; 1.54 +#endif 1.55 + 1.56 + return NS_OK; 1.57 +} 1.58 + 1.59 +NS_IMETHODIMP 1.60 +nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues, 1.61 + uint32_t aWhichFlags) 1.62 +{ 1.63 + NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags), 1.64 + "aWhichFlags should only be compression flag"); 1.65 + 1.66 + if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) { 1.67 + // updating the compression flag is allowed 1.68 + if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) { 1.69 + // 'compressed' means 'prime' style in App. G, TeXbook 1.70 + mPresentationData.flags |= NS_MATHML_COMPRESSED; 1.71 + } 1.72 + // no else. the flag is sticky. it retains its value once it is set 1.73 + } 1.74 + return NS_OK; 1.75 +} 1.76 + 1.77 +// Helper to give a style context suitable for doing the stretching of 1.78 +// a MathMLChar. Frame classes that use this should ensure that the 1.79 +// extra leaf style contexts given to the MathMLChars are accessible to 1.80 +// the Style System via the Get/Set AdditionalStyleContext() APIs. 1.81 +/* static */ void 1.82 +nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext, 1.83 + nsIContent* aContent, 1.84 + nsStyleContext* aParentStyleContext, 1.85 + nsMathMLChar* aMathMLChar) 1.86 +{ 1.87 + nsCSSPseudoElements::Type pseudoType = 1.88 + nsCSSPseudoElements::ePseudo_mozMathAnonymous; // savings 1.89 + nsRefPtr<nsStyleContext> newStyleContext; 1.90 + newStyleContext = aPresContext->StyleSet()-> 1.91 + ResolvePseudoElementStyle(aContent->AsElement(), pseudoType, 1.92 + aParentStyleContext, nullptr); 1.93 + 1.94 + aMathMLChar->SetStyleContext(newStyleContext); 1.95 +} 1.96 + 1.97 +/* static */ void 1.98 +nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame, 1.99 + nsEmbellishData& aEmbellishData) 1.100 +{ 1.101 + // initialize OUT params 1.102 + aEmbellishData.flags = 0; 1.103 + aEmbellishData.coreFrame = nullptr; 1.104 + aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; 1.105 + aEmbellishData.leadingSpace = 0; 1.106 + aEmbellishData.trailingSpace = 0; 1.107 + 1.108 + if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) { 1.109 + nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame); 1.110 + if (mathMLFrame) { 1.111 + mathMLFrame->GetEmbellishData(aEmbellishData); 1.112 + } 1.113 + } 1.114 +} 1.115 + 1.116 +// helper to get the presentation data of a frame, by possibly walking up 1.117 +// the frame hierarchy if we happen to be surrounded by non-MathML frames. 1.118 +/* static */ void 1.119 +nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame, 1.120 + nsPresentationData& aPresentationData, 1.121 + bool aClimbTree) 1.122 +{ 1.123 + // initialize OUT params 1.124 + aPresentationData.flags = 0; 1.125 + aPresentationData.baseFrame = nullptr; 1.126 + 1.127 + nsIFrame* frame = aFrame; 1.128 + while (frame) { 1.129 + if (frame->IsFrameOfType(nsIFrame::eMathML)) { 1.130 + nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame); 1.131 + if (mathMLFrame) { 1.132 + mathMLFrame->GetPresentationData(aPresentationData); 1.133 + break; 1.134 + } 1.135 + } 1.136 + // stop if the caller doesn't want to lookup beyond the frame 1.137 + if (!aClimbTree) { 1.138 + break; 1.139 + } 1.140 + // stop if we reach the root <math> tag 1.141 + nsIContent* content = frame->GetContent(); 1.142 + NS_ASSERTION(content || !frame->GetParent(), // no assert for the root 1.143 + "dangling frame without a content node"); 1.144 + if (!content) 1.145 + break; 1.146 + 1.147 + if (content->Tag() == nsGkAtoms::math) { 1.148 + break; 1.149 + } 1.150 + frame = frame->GetParent(); 1.151 + } 1.152 + NS_WARN_IF_FALSE(frame && frame->GetContent(), 1.153 + "bad MathML markup - could not find the top <math> element"); 1.154 +} 1.155 + 1.156 +/* static */ void 1.157 +nsMathMLFrame::GetRuleThickness(nsRenderingContext& aRenderingContext, 1.158 + nsFontMetrics* aFontMetrics, 1.159 + nscoord& aRuleThickness) 1.160 +{ 1.161 + // get the bounding metrics of the overbar char, the rendering context 1.162 + // is assumed to have been set with the font of the current style context 1.163 + NS_ASSERTION(aRenderingContext.FontMetrics()->Font(). 1.164 + Equals(aFontMetrics->Font()), 1.165 + "unexpected state"); 1.166 + 1.167 + nscoord xHeight = aFontMetrics->XHeight(); 1.168 + char16_t overBar = 0x00AF; 1.169 + nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&overBar, 1); 1.170 + aRuleThickness = bm.ascent + bm.descent; 1.171 + if (aRuleThickness <= 0 || aRuleThickness >= xHeight) { 1.172 + // fall-back to the other version 1.173 + GetRuleThickness(aFontMetrics, aRuleThickness); 1.174 + } 1.175 +} 1.176 + 1.177 +/* static */ void 1.178 +nsMathMLFrame::GetAxisHeight(nsRenderingContext& aRenderingContext, 1.179 + nsFontMetrics* aFontMetrics, 1.180 + nscoord& aAxisHeight) 1.181 +{ 1.182 + // get the bounding metrics of the minus sign, the rendering context 1.183 + // is assumed to have been set with the font of the current style context 1.184 + NS_ASSERTION(aRenderingContext.FontMetrics()->Font(). 1.185 + Equals(aFontMetrics->Font()), 1.186 + "unexpected state"); 1.187 + 1.188 + nscoord xHeight = aFontMetrics->XHeight(); 1.189 + char16_t minus = 0x2212; // not '-', but official Unicode minus sign 1.190 + nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&minus, 1); 1.191 + aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2; 1.192 + if (aAxisHeight <= 0 || aAxisHeight >= xHeight) { 1.193 + // fall-back to the other version 1.194 + GetAxisHeight(aFontMetrics, aAxisHeight); 1.195 + } 1.196 +} 1.197 + 1.198 +/* static */ nscoord 1.199 +nsMathMLFrame::CalcLength(nsPresContext* aPresContext, 1.200 + nsStyleContext* aStyleContext, 1.201 + const nsCSSValue& aCSSValue) 1.202 +{ 1.203 + NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit"); 1.204 + 1.205 + if (aCSSValue.IsFixedLengthUnit()) { 1.206 + return aCSSValue.GetFixedLength(aPresContext); 1.207 + } 1.208 + if (aCSSValue.IsPixelLengthUnit()) { 1.209 + return aCSSValue.GetPixelLength(); 1.210 + } 1.211 + 1.212 + nsCSSUnit unit = aCSSValue.GetUnit(); 1.213 + 1.214 + if (eCSSUnit_EM == unit) { 1.215 + const nsStyleFont* font = aStyleContext->StyleFont(); 1.216 + return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size); 1.217 + } 1.218 + else if (eCSSUnit_XHeight == unit) { 1.219 + nsRefPtr<nsFontMetrics> fm; 1.220 + nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext, 1.221 + getter_AddRefs(fm)); 1.222 + nscoord xHeight = fm->XHeight(); 1.223 + return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight); 1.224 + } 1.225 + 1.226 + // MathML doesn't specify other CSS units such as rem or ch 1.227 + NS_ERROR("Unsupported unit"); 1.228 + return 0; 1.229 +} 1.230 + 1.231 +/* static */ void 1.232 +nsMathMLFrame::ParseNumericValue(const nsString& aString, 1.233 + nscoord* aLengthValue, 1.234 + uint32_t aFlags, 1.235 + nsPresContext* aPresContext, 1.236 + nsStyleContext* aStyleContext) 1.237 +{ 1.238 + nsCSSValue cssValue; 1.239 + 1.240 + if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags, 1.241 + aPresContext->Document())) { 1.242 + // Invalid attribute value. aLengthValue remains unchanged, so the default 1.243 + // length value is used. 1.244 + return; 1.245 + } 1.246 + 1.247 + nsCSSUnit unit = cssValue.GetUnit(); 1.248 + 1.249 + if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) { 1.250 + // Relative units. A multiple of the default length value is used. 1.251 + *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ? 1.252 + cssValue.GetPercentValue() : 1.253 + cssValue.GetFloatValue())); 1.254 + return; 1.255 + } 1.256 + 1.257 + // Absolute units. 1.258 + *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue); 1.259 +} 1.260 + 1.261 +// ================ 1.262 +// Utils to map attributes into CSS rules (work-around to bug 69409 which 1.263 +// is not scheduled to be fixed anytime soon) 1.264 +// 1.265 + 1.266 +struct 1.267 +nsCSSMapping { 1.268 + int32_t compatibility; 1.269 + const nsIAtom* attrAtom; 1.270 + const char* cssProperty; 1.271 +}; 1.272 + 1.273 +#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) 1.274 +class nsDisplayMathMLBoundingMetrics : public nsDisplayItem { 1.275 +public: 1.276 + nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder, 1.277 + nsIFrame* aFrame, const nsRect& aRect) 1.278 + : nsDisplayItem(aBuilder, aFrame), mRect(aRect) { 1.279 + MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics); 1.280 + } 1.281 +#ifdef NS_BUILD_REFCNT_LOGGING 1.282 + virtual ~nsDisplayMathMLBoundingMetrics() { 1.283 + MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics); 1.284 + } 1.285 +#endif 1.286 + 1.287 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.288 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.289 + NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS) 1.290 +private: 1.291 + nsRect mRect; 1.292 +}; 1.293 + 1.294 +void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder, 1.295 + nsRenderingContext* aCtx) 1.296 +{ 1.297 + aCtx->SetColor(NS_RGB(0,0,255)); 1.298 + aCtx->DrawRect(mRect + ToReferenceFrame()); 1.299 +} 1.300 + 1.301 +nsresult 1.302 +nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder, 1.303 + nsIFrame* aFrame, const nsPoint& aPt, 1.304 + const nsBoundingMetrics& aMetrics, 1.305 + const nsDisplayListSet& aLists) { 1.306 + if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags)) 1.307 + return NS_OK; 1.308 + 1.309 + nscoord x = aPt.x + aMetrics.leftBearing; 1.310 + nscoord y = aPt.y - aMetrics.ascent; 1.311 + nscoord w = aMetrics.rightBearing - aMetrics.leftBearing; 1.312 + nscoord h = aMetrics.ascent + aMetrics.descent; 1.313 + 1.314 + return aLists.Content()->AppendNewToTop(new (aBuilder) 1.315 + nsDisplayMathMLBoundingMetrics(aBuilder, this, nsRect(x,y,w,h))); 1.316 +} 1.317 +#endif 1.318 + 1.319 +class nsDisplayMathMLBar : public nsDisplayItem { 1.320 +public: 1.321 + nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder, 1.322 + nsIFrame* aFrame, const nsRect& aRect) 1.323 + : nsDisplayItem(aBuilder, aFrame), mRect(aRect) { 1.324 + MOZ_COUNT_CTOR(nsDisplayMathMLBar); 1.325 + } 1.326 +#ifdef NS_BUILD_REFCNT_LOGGING 1.327 + virtual ~nsDisplayMathMLBar() { 1.328 + MOZ_COUNT_DTOR(nsDisplayMathMLBar); 1.329 + } 1.330 +#endif 1.331 + 1.332 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.333 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.334 + NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR) 1.335 +private: 1.336 + nsRect mRect; 1.337 +}; 1.338 + 1.339 +void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder, 1.340 + nsRenderingContext* aCtx) 1.341 +{ 1.342 + // paint the bar with the current text color 1.343 + aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); 1.344 + aCtx->FillRect(mRect + ToReferenceFrame()); 1.345 +} 1.346 + 1.347 +void 1.348 +nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder, 1.349 + nsIFrame* aFrame, const nsRect& aRect, 1.350 + const nsDisplayListSet& aLists) { 1.351 + if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) 1.352 + return; 1.353 + 1.354 + aLists.Content()->AppendNewToTop(new (aBuilder) 1.355 + nsDisplayMathMLBar(aBuilder, aFrame, aRect)); 1.356 +}