forked from mirrors/gecko-dev
		
	 d9a694f78f
			
		
	
	
		d9a694f78f
		
	
	
	
	
		
			
			While looking at moving the flag around I realized that the only reason we have FontMetricsProvider and co is because we didn't have access to the per-document font-prefs cache. That's trivial to fix tho, so do that and simplify the setup for font queries even more. Differential Revision: https://phabricator.services.mozilla.com/D157589
		
			
				
	
	
		
			383 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsMathMLFrame.h"
 | |
| 
 | |
| #include "gfxContext.h"
 | |
| #include "gfxUtils.h"
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "nsCSSValue.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsNameSpaceManager.h"
 | |
| #include "nsMathMLChar.h"
 | |
| #include "nsCSSPseudoElements.h"
 | |
| #include "mozilla/dom/MathMLElement.h"
 | |
| #include "gfxMathTable.h"
 | |
| #include "nsPresContextInlines.h"
 | |
| 
 | |
| // used to map attributes into CSS rules
 | |
| #include "mozilla/ServoStyleSet.h"
 | |
| #include "nsDisplayList.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::gfx;
 | |
| 
 | |
| eMathMLFrameType nsMathMLFrame::GetMathMLFrameType() {
 | |
|   // see if it is an embellished operator (mapped to 'Op' in TeX)
 | |
|   if (mEmbellishData.coreFrame)
 | |
|     return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
 | |
| 
 | |
|   // if it has a prescribed base, fetch the type from there
 | |
|   if (mPresentationData.baseFrame)
 | |
|     return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
 | |
| 
 | |
|   // everything else is treated as ordinary (mapped to 'Ord' in TeX)
 | |
|   return eMathMLFrameType_Ordinary;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent) {
 | |
|   mEmbellishData.flags = 0;
 | |
|   mEmbellishData.coreFrame = nullptr;
 | |
|   mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
 | |
|   mEmbellishData.leadingSpace = 0;
 | |
|   mEmbellishData.trailingSpace = 0;
 | |
| 
 | |
|   mPresentationData.flags = 0;
 | |
|   mPresentationData.baseFrame = nullptr;
 | |
| 
 | |
|   // by default, just inherit the display of our parent
 | |
|   nsPresentationData parentData;
 | |
|   GetPresentationDataFrom(aParent, parentData);
 | |
| 
 | |
| #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
 | |
|   mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
 | |
| #endif
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues,
 | |
|                                       uint32_t aWhichFlags) {
 | |
|   NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags) ||
 | |
|                    NS_MATHML_IS_DTLS_SET(aWhichFlags),
 | |
|                "aWhichFlags should only be compression or dtls flag");
 | |
| 
 | |
|   if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) {
 | |
|     // updating the compression flag is allowed
 | |
|     if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
 | |
|       // 'compressed' means 'prime' style in App. G, TeXbook
 | |
|       mPresentationData.flags |= NS_MATHML_COMPRESSED;
 | |
|     }
 | |
|     // no else. the flag is sticky. it retains its value once it is set
 | |
|   }
 | |
|   // These flags determine whether the dtls font feature settings should
 | |
|   // be applied.
 | |
|   if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
 | |
|     if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
 | |
|       mPresentationData.flags |= NS_MATHML_DTLS;
 | |
|     } else {
 | |
|       mPresentationData.flags &= ~NS_MATHML_DTLS;
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame,
 | |
|                                          nsEmbellishData& aEmbellishData) {
 | |
|   // initialize OUT params
 | |
|   aEmbellishData.flags = 0;
 | |
|   aEmbellishData.coreFrame = nullptr;
 | |
|   aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
 | |
|   aEmbellishData.leadingSpace = 0;
 | |
|   aEmbellishData.trailingSpace = 0;
 | |
| 
 | |
|   if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
 | |
|     nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
 | |
|     if (mathMLFrame) {
 | |
|       mathMLFrame->GetEmbellishData(aEmbellishData);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // helper to get the presentation data of a frame, by possibly walking up
 | |
| // the frame hierarchy if we happen to be surrounded by non-MathML frames.
 | |
| /* static */
 | |
| void nsMathMLFrame::GetPresentationDataFrom(
 | |
|     nsIFrame* aFrame, nsPresentationData& aPresentationData, bool aClimbTree) {
 | |
|   // initialize OUT params
 | |
|   aPresentationData.flags = 0;
 | |
|   aPresentationData.baseFrame = nullptr;
 | |
| 
 | |
|   nsIFrame* frame = aFrame;
 | |
|   while (frame) {
 | |
|     if (frame->IsFrameOfType(nsIFrame::eMathML)) {
 | |
|       nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
 | |
|       if (mathMLFrame) {
 | |
|         mathMLFrame->GetPresentationData(aPresentationData);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     // stop if the caller doesn't want to lookup beyond the frame
 | |
|     if (!aClimbTree) {
 | |
|       break;
 | |
|     }
 | |
|     // stop if we reach the root <math> tag
 | |
|     nsIContent* content = frame->GetContent();
 | |
|     NS_ASSERTION(content || !frame->GetParent(),  // no assert for the root
 | |
|                  "dangling frame without a content node");
 | |
|     if (!content) break;
 | |
| 
 | |
|     if (content->IsMathMLElement(nsGkAtoms::math)) {
 | |
|       break;
 | |
|     }
 | |
|     frame = frame->GetParent();
 | |
|   }
 | |
|   NS_WARNING_ASSERTION(
 | |
|       frame && frame->GetContent(),
 | |
|       "bad MathML markup - could not find the top <math> element");
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void nsMathMLFrame::GetRuleThickness(DrawTarget* aDrawTarget,
 | |
|                                      nsFontMetrics* aFontMetrics,
 | |
|                                      nscoord& aRuleThickness) {
 | |
|   nscoord xHeight = aFontMetrics->XHeight();
 | |
|   char16_t overBar = 0x00AF;
 | |
|   nsBoundingMetrics bm = nsLayoutUtils::AppUnitBoundsOfString(
 | |
|       &overBar, 1, *aFontMetrics, aDrawTarget);
 | |
|   aRuleThickness = bm.ascent + bm.descent;
 | |
|   if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
 | |
|     // fall-back to the other version
 | |
|     GetRuleThickness(aFontMetrics, aRuleThickness);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
 | |
|                                   nsFontMetrics* aFontMetrics,
 | |
|                                   nscoord& aAxisHeight) {
 | |
|   RefPtr<gfxFont> mathFont =
 | |
|       aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
 | |
|   if (mathFont) {
 | |
|     aAxisHeight = mathFont->MathTable()->Constant(
 | |
|         gfxMathTable::AxisHeight, aFontMetrics->AppUnitsPerDevPixel());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nscoord xHeight = aFontMetrics->XHeight();
 | |
|   char16_t minus = 0x2212;  // not '-', but official Unicode minus sign
 | |
|   nsBoundingMetrics bm = nsLayoutUtils::AppUnitBoundsOfString(
 | |
|       &minus, 1, *aFontMetrics, aDrawTarget);
 | |
|   aAxisHeight = bm.ascent - (bm.ascent + bm.descent) / 2;
 | |
|   if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
 | |
|     // fall-back to the other version
 | |
|     GetAxisHeight(aFontMetrics, aAxisHeight);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| nscoord nsMathMLFrame::CalcLength(nsPresContext* aPresContext,
 | |
|                                   ComputedStyle* aComputedStyle,
 | |
|                                   const nsCSSValue& aCSSValue,
 | |
|                                   float aFontSizeInflation) {
 | |
|   NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
 | |
| 
 | |
|   if (aCSSValue.IsPixelLengthUnit()) {
 | |
|     return aCSSValue.GetPixelLength();
 | |
|   }
 | |
| 
 | |
|   nsCSSUnit unit = aCSSValue.GetUnit();
 | |
| 
 | |
|   if (eCSSUnit_EM == unit) {
 | |
|     const nsStyleFont* font = aComputedStyle->StyleFont();
 | |
|     return font->mFont.size.ScaledBy(aCSSValue.GetFloatValue()).ToAppUnits();
 | |
|   }
 | |
| 
 | |
|   if (eCSSUnit_XHeight == unit) {
 | |
|     RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForComputedStyle(
 | |
|         aComputedStyle, aPresContext, aFontSizeInflation);
 | |
|     nscoord xHeight = fm->XHeight();
 | |
|     return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
 | |
|   }
 | |
| 
 | |
|   // MathML doesn't specify other CSS units such as rem or ch
 | |
|   NS_ERROR("Unsupported unit");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void nsMathMLFrame::GetSubDropFromChild(nsIFrame* aChild, nscoord& aSubDrop,
 | |
|                                         float aFontSizeInflation) {
 | |
|   RefPtr<nsFontMetrics> fm =
 | |
|       nsLayoutUtils::GetFontMetricsForFrame(aChild, aFontSizeInflation);
 | |
|   GetSubDrop(fm, aSubDrop);
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void nsMathMLFrame::GetSupDropFromChild(nsIFrame* aChild, nscoord& aSupDrop,
 | |
|                                         float aFontSizeInflation) {
 | |
|   RefPtr<nsFontMetrics> fm =
 | |
|       nsLayoutUtils::GetFontMetricsForFrame(aChild, aFontSizeInflation);
 | |
|   GetSupDrop(fm, aSupDrop);
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void nsMathMLFrame::ParseNumericValue(const nsString& aString,
 | |
|                                       nscoord* aLengthValue, uint32_t aFlags,
 | |
|                                       nsPresContext* aPresContext,
 | |
|                                       ComputedStyle* aComputedStyle,
 | |
|                                       float aFontSizeInflation) {
 | |
|   nsCSSValue cssValue;
 | |
| 
 | |
|   if (!dom::MathMLElement::ParseNumericValue(aString, cssValue, aFlags,
 | |
|                                              aPresContext->Document())) {
 | |
|     // Invalid attribute value. aLengthValue remains unchanged, so the default
 | |
|     // length value is used.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCSSUnit unit = cssValue.GetUnit();
 | |
| 
 | |
|   if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
 | |
|     // Relative units. A multiple of the default length value is used.
 | |
|     *aLengthValue = NSToCoordRound(
 | |
|         *aLengthValue * (unit == eCSSUnit_Percent ? cssValue.GetPercentValue()
 | |
|                                                   : cssValue.GetFloatValue()));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Absolute units.
 | |
|   *aLengthValue =
 | |
|       CalcLength(aPresContext, aComputedStyle, cssValue, aFontSizeInflation);
 | |
| }
 | |
| 
 | |
| namespace mozilla {
 | |
| #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
 | |
| class nsDisplayMathMLBoundingMetrics final : public nsDisplayItem {
 | |
|  public:
 | |
|   nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder,
 | |
|                                  nsIFrame* aFrame, const nsRect& aRect)
 | |
|       : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
 | |
|     MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics);
 | |
|   }
 | |
|   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayMathMLBoundingMetrics)
 | |
| 
 | |
|   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
 | |
|   NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
 | |
|  private:
 | |
|   nsRect mRect;
 | |
| };
 | |
| 
 | |
| void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
 | |
|                                            gfxContext* aCtx) {
 | |
|   DrawTarget* drawTarget = aCtx->GetDrawTarget();
 | |
|   Rect r = NSRectToRect(mRect + ToReferenceFrame(),
 | |
|                         mFrame->PresContext()->AppUnitsPerDevPixel());
 | |
|   ColorPattern blue(ToDeviceColor(Color(0.f, 0.f, 1.f, 1.f)));
 | |
|   drawTarget->StrokeRect(r, blue);
 | |
| }
 | |
| 
 | |
| void nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder,
 | |
|                                            nsIFrame* aFrame, const nsPoint& aPt,
 | |
|                                            const nsBoundingMetrics& aMetrics,
 | |
|                                            const nsDisplayListSet& aLists) {
 | |
|   if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags)) return;
 | |
| 
 | |
|   nscoord x = aPt.x + aMetrics.leftBearing;
 | |
|   nscoord y = aPt.y - aMetrics.ascent;
 | |
|   nscoord w = aMetrics.rightBearing - aMetrics.leftBearing;
 | |
|   nscoord h = aMetrics.ascent + aMetrics.descent;
 | |
| 
 | |
|   aLists.Content()->AppendNewToTop<nsDisplayMathMLBoundingMetrics>(
 | |
|       aBuilder, aFrame, nsRect(x, y, w, h));
 | |
| }
 | |
| #endif
 | |
| 
 | |
| class nsDisplayMathMLBar final : public nsPaintedDisplayItem {
 | |
|  public:
 | |
|   nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
 | |
|                      const nsRect& aRect)
 | |
|       : nsPaintedDisplayItem(aBuilder, aFrame), mRect(aRect) {
 | |
|     MOZ_COUNT_CTOR(nsDisplayMathMLBar);
 | |
|   }
 | |
|   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayMathMLBar)
 | |
| 
 | |
|   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
 | |
|   NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
 | |
| 
 | |
|  private:
 | |
|   nsRect mRect;
 | |
| };
 | |
| 
 | |
| void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
 | |
|                                gfxContext* aCtx) {
 | |
|   // paint the bar with the current text color
 | |
|   DrawTarget* drawTarget = aCtx->GetDrawTarget();
 | |
|   Rect rect = NSRectToNonEmptySnappedRect(
 | |
|       mRect + ToReferenceFrame(), mFrame->PresContext()->AppUnitsPerDevPixel(),
 | |
|       *drawTarget);
 | |
|   ColorPattern color(ToDeviceColor(
 | |
|       mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
 | |
|   drawTarget->FillRect(rect, color);
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 | |
| 
 | |
| void nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
 | |
|                                const nsRect& aRect,
 | |
|                                const nsDisplayListSet& aLists,
 | |
|                                uint32_t aIndex) {
 | |
|   if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) return;
 | |
| 
 | |
|   aLists.Content()->AppendNewToTopWithIndex<nsDisplayMathMLBar>(
 | |
|       aBuilder, aFrame, aIndex, aRect);
 | |
| }
 | |
| 
 | |
| void nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
 | |
|                                          bool aDisplayStyle,
 | |
|                                          nscoord& aRadicalRuleThickness,
 | |
|                                          nscoord& aRadicalExtraAscender,
 | |
|                                          nscoord& aRadicalVerticalGap) {
 | |
|   nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
 | |
|   RefPtr<gfxFont> mathFont =
 | |
|       aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
 | |
| 
 | |
|   // get the radical rulethickness
 | |
|   if (mathFont) {
 | |
|     aRadicalRuleThickness = mathFont->MathTable()->Constant(
 | |
|         gfxMathTable::RadicalRuleThickness, oneDevPixel);
 | |
|   } else {
 | |
|     GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
 | |
|   }
 | |
| 
 | |
|   // get the leading to be left at the top of the resulting frame
 | |
|   if (mathFont) {
 | |
|     aRadicalExtraAscender = mathFont->MathTable()->Constant(
 | |
|         gfxMathTable::RadicalExtraAscender, oneDevPixel);
 | |
|   } else {
 | |
|     // This seems more reliable than using aFontMetrics->GetLeading() on
 | |
|     // suspicious fonts.
 | |
|     nscoord em;
 | |
|     GetEmHeight(aFontMetrics, em);
 | |
|     aRadicalExtraAscender = nscoord(0.2f * em);
 | |
|   }
 | |
| 
 | |
|   // get the clearance between rule and content
 | |
|   if (mathFont) {
 | |
|     aRadicalVerticalGap = mathFont->MathTable()->Constant(
 | |
|         aDisplayStyle ? gfxMathTable::RadicalDisplayStyleVerticalGap
 | |
|                       : gfxMathTable::RadicalVerticalGap,
 | |
|         oneDevPixel);
 | |
|   } else {
 | |
|     // Rule 11, App. G, TeXbook
 | |
|     aRadicalVerticalGap =
 | |
|         aRadicalRuleThickness +
 | |
|         (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;
 | |
|   }
 | |
| }
 |