tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsMathMLFrame.cpp (11867B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsMathMLFrame.h"
      8 
      9 #include "gfxContext.h"
     10 #include "gfxMathTable.h"
     11 #include "gfxUtils.h"
     12 #include "mozilla/StaticPrefs_mathml.h"
     13 #include "mozilla/dom/MathMLElement.h"
     14 #include "mozilla/gfx/2D.h"
     15 #include "nsCSSPseudoElements.h"
     16 #include "nsCSSValue.h"
     17 #include "nsLayoutUtils.h"
     18 #include "nsMathMLChar.h"
     19 #include "nsNameSpaceManager.h"
     20 #include "nsPresContextInlines.h"
     21 
     22 // used for parsing CSS units
     23 #include "mozilla/dom/SVGAnimatedLength.h"
     24 #include "mozilla/dom/SVGLength.h"
     25 
     26 // used to map attributes into CSS rules
     27 #include "mozilla/ServoStyleSet.h"
     28 #include "nsDisplayList.h"
     29 
     30 using namespace mozilla;
     31 using namespace mozilla::gfx;
     32 
     33 MathMLFrameType nsMathMLFrame::GetMathMLFrameType() {
     34  // see if it is an embellished operator (mapped to 'Op' in TeX)
     35  if (mEmbellishData.coreFrame) {
     36    return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
     37  }
     38 
     39  // if it has a prescribed base, fetch the type from there
     40  if (mPresentationData.baseFrame) {
     41    return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
     42  }
     43 
     44  // everything else is treated as ordinary (mapped to 'Ord' in TeX)
     45  return MathMLFrameType::Ordinary;
     46 }
     47 
     48 NS_IMETHODIMP
     49 nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent) {
     50  mEmbellishData.flags.clear();
     51  mEmbellishData.coreFrame = nullptr;
     52  mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
     53  mEmbellishData.leadingSpace = 0;
     54  mEmbellishData.trailingSpace = 0;
     55 
     56  mPresentationData.flags.clear();
     57  mPresentationData.baseFrame = nullptr;
     58 
     59  // by default, just inherit the display of our parent
     60  nsPresentationData parentData;
     61  GetPresentationDataFrom(aParent, parentData);
     62 
     63  return NS_OK;
     64 }
     65 
     66 NS_IMETHODIMP
     67 nsMathMLFrame::UpdatePresentationData(MathMLPresentationFlags aFlagsValues,
     68                                      MathMLPresentationFlags aWhichFlags) {
     69  NS_ASSERTION(aWhichFlags.contains(MathMLPresentationFlag::Compressed) ||
     70                   aWhichFlags.contains(MathMLPresentationFlag::Dtls),
     71               "aWhichFlags should only be compression or dtls flag");
     72 
     73  if (!StaticPrefs::mathml_math_shift_enabled() &&
     74      aWhichFlags.contains(MathMLPresentationFlag::Compressed)) {
     75    // updating the compression flag is allowed
     76    if (aFlagsValues.contains(MathMLPresentationFlag::Compressed)) {
     77      // 'compressed' means 'prime' style in App. G, TeXbook
     78      mPresentationData.flags += MathMLPresentationFlag::Compressed;
     79    }
     80    // no else. the flag is sticky. it retains its value once it is set
     81  }
     82  // These flags determine whether the dtls font feature settings should
     83  // be applied.
     84  if (aWhichFlags.contains(MathMLPresentationFlag::Dtls)) {
     85    if (aFlagsValues.contains(MathMLPresentationFlag::Dtls)) {
     86      mPresentationData.flags += MathMLPresentationFlag::Dtls;
     87    } else {
     88      mPresentationData.flags -= MathMLPresentationFlag::Dtls;
     89    }
     90  }
     91  return NS_OK;
     92 }
     93 
     94 /* static */
     95 void nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame,
     96                                         nsEmbellishData& aEmbellishData) {
     97  // initialize OUT params
     98  aEmbellishData.flags.clear();
     99  aEmbellishData.coreFrame = nullptr;
    100  aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
    101  aEmbellishData.leadingSpace = 0;
    102  aEmbellishData.trailingSpace = 0;
    103 
    104  if (aFrame && aFrame->IsMathMLFrame()) {
    105    nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
    106    if (mathMLFrame) {
    107      mathMLFrame->GetEmbellishData(aEmbellishData);
    108    }
    109  }
    110 }
    111 
    112 // helper to get the presentation data of a frame, by possibly walking up
    113 // the frame hierarchy if we happen to be surrounded by non-MathML frames.
    114 /* static */
    115 void nsMathMLFrame::GetPresentationDataFrom(
    116    nsIFrame* aFrame, nsPresentationData& aPresentationData, bool aClimbTree) {
    117  // initialize OUT params
    118  aPresentationData.flags.clear();
    119  aPresentationData.baseFrame = nullptr;
    120 
    121  nsIFrame* frame = aFrame;
    122  while (frame) {
    123    if (frame->IsMathMLFrame()) {
    124      nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
    125      if (mathMLFrame) {
    126        mathMLFrame->GetPresentationData(aPresentationData);
    127        break;
    128      }
    129    }
    130    // stop if the caller doesn't want to lookup beyond the frame
    131    if (!aClimbTree) {
    132      break;
    133    }
    134    // stop if we reach the root <math> tag
    135    nsIContent* content = frame->GetContent();
    136    NS_ASSERTION(content || !frame->GetParent(),  // no assert for the root
    137                 "dangling frame without a content node");
    138    if (!content) {
    139      break;
    140    }
    141 
    142    if (content->IsMathMLElement(nsGkAtoms::math)) {
    143      break;
    144    }
    145    frame = frame->GetParent();
    146  }
    147  NS_WARNING_ASSERTION(
    148      frame && frame->GetContent(),
    149      "bad MathML markup - could not find the top <math> element");
    150 }
    151 
    152 /* static */
    153 void nsMathMLFrame::GetRuleThickness(DrawTarget* aDrawTarget,
    154                                     nsFontMetrics* aFontMetrics,
    155                                     nscoord& aRuleThickness) {
    156  nscoord xHeight = aFontMetrics->XHeight();
    157  char16_t overBar = 0x00AF;
    158  nsBoundingMetrics bm = nsLayoutUtils::AppUnitBoundsOfString(
    159      &overBar, 1, *aFontMetrics, aDrawTarget);
    160  aRuleThickness = bm.ascent + bm.descent;
    161  if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
    162    // fall-back to the other version
    163    GetRuleThickness(aFontMetrics, aRuleThickness);
    164  }
    165 }
    166 
    167 /* static */
    168 void nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
    169                                  nsFontMetrics* aFontMetrics,
    170                                  nscoord& aAxisHeight) {
    171  RefPtr<gfxFont> mathFont =
    172      aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
    173  if (mathFont) {
    174    aAxisHeight = mathFont->MathTable()->Constant(
    175        gfxMathTable::AxisHeight, aFontMetrics->AppUnitsPerDevPixel());
    176    return;
    177  }
    178 
    179  nscoord xHeight = aFontMetrics->XHeight();
    180  char16_t minus = 0x2212;  // not '-', but official Unicode minus sign
    181  nsBoundingMetrics bm = nsLayoutUtils::AppUnitBoundsOfString(
    182      &minus, 1, *aFontMetrics, aDrawTarget);
    183  aAxisHeight = bm.ascent - (bm.ascent + bm.descent) / 2;
    184  if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
    185    // fall-back to the other version
    186    GetAxisHeight(aFontMetrics, aAxisHeight);
    187  }
    188 }
    189 
    190 /* static */
    191 nscoord nsMathMLFrame::CalcLength(const nsCSSValue& aCSSValue,
    192                                  float aFontSizeInflation, nsIFrame* aFrame) {
    193  NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
    194 
    195  nsCSSUnit unit = aCSSValue.GetUnit();
    196  mozilla::dom::NonSVGFrameUserSpaceMetrics userSpaceMetrics(aFrame);
    197 
    198  // The axis is only relevant for percentages, so it doesn't matter what we use
    199  // here.
    200  auto axis = SVGContentUtils::X;
    201 
    202  return nsPresContext::CSSPixelsToAppUnits(
    203      aCSSValue.GetFloatValue() *
    204      SVGLength::GetPixelsPerCSSUnit(userSpaceMetrics, unit, axis,
    205                                     /* aApplyZoom = */ true));
    206 }
    207 
    208 /* static */
    209 void nsMathMLFrame::GetSubDropFromChild(nsIFrame* aChild, nscoord& aSubDrop,
    210                                        float aFontSizeInflation) {
    211  RefPtr<nsFontMetrics> fm =
    212      nsLayoutUtils::GetFontMetricsForFrame(aChild, aFontSizeInflation);
    213  GetSubDrop(fm, aSubDrop);
    214 }
    215 
    216 /* static */
    217 void nsMathMLFrame::GetSupDropFromChild(nsIFrame* aChild, nscoord& aSupDrop,
    218                                        float aFontSizeInflation) {
    219  RefPtr<nsFontMetrics> fm =
    220      nsLayoutUtils::GetFontMetricsForFrame(aChild, aFontSizeInflation);
    221  GetSupDrop(fm, aSupDrop);
    222 }
    223 
    224 /* static */
    225 void nsMathMLFrame::ParseAndCalcNumericValue(const nsString& aString,
    226                                             nscoord* aLengthValue,
    227                                             uint32_t aFlags,
    228                                             float aFontSizeInflation,
    229                                             nsIFrame* aFrame) {
    230  nsCSSValue cssValue;
    231 
    232  if (!dom::MathMLElement::ParseNumericValue(
    233          aString, cssValue, aFlags, aFrame->PresContext()->Document())) {
    234    // Invalid attribute value. aLengthValue remains unchanged, so the default
    235    // length value is used.
    236    return;
    237  }
    238 
    239  nsCSSUnit unit = cssValue.GetUnit();
    240 
    241  // Since we're reusing the SVG code to calculate unit lengths,
    242  // which handles percentage values specially, we have to deal
    243  // with percentages early on.
    244  if (unit == eCSSUnit_Percent) {
    245    *aLengthValue = NSToCoordRound(*aLengthValue * cssValue.GetPercentValue());
    246    return;
    247  }
    248 
    249  *aLengthValue = CalcLength(cssValue, aFontSizeInflation, aFrame);
    250 }
    251 
    252 namespace mozilla {
    253 
    254 class nsDisplayMathMLBar final : public nsPaintedDisplayItem {
    255 public:
    256  nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
    257                     const nsRect& aRect)
    258      : nsPaintedDisplayItem(aBuilder, aFrame), mRect(aRect) {
    259    MOZ_COUNT_CTOR(nsDisplayMathMLBar);
    260  }
    261 
    262  MOZ_COUNTED_DTOR_FINAL(nsDisplayMathMLBar)
    263 
    264  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
    265  NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
    266 
    267 private:
    268  nsRect mRect;
    269 };
    270 
    271 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
    272                               gfxContext* aCtx) {
    273  // paint the bar with the current text color
    274  DrawTarget* drawTarget = aCtx->GetDrawTarget();
    275  Rect rect = NSRectToNonEmptySnappedRect(
    276      mRect + ToReferenceFrame(), mFrame->PresContext()->AppUnitsPerDevPixel(),
    277      *drawTarget);
    278  ColorPattern color(ToDeviceColor(
    279      mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
    280  drawTarget->FillRect(rect, color);
    281 }
    282 
    283 }  // namespace mozilla
    284 
    285 void nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
    286                               const nsRect& aRect,
    287                               const nsDisplayListSet& aLists,
    288                               uint32_t aIndex) {
    289  if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) {
    290    return;
    291  }
    292 
    293  aLists.Content()->AppendNewToTopWithIndex<nsDisplayMathMLBar>(
    294      aBuilder, aFrame, aIndex, aRect);
    295 }
    296 
    297 void nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
    298                                         bool aDisplayStyle,
    299                                         nscoord& aRadicalRuleThickness,
    300                                         nscoord& aRadicalExtraAscender,
    301                                         nscoord& aRadicalVerticalGap) {
    302  nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
    303  RefPtr<gfxFont> mathFont =
    304      aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
    305 
    306  // get the radical rulethickness
    307  if (mathFont) {
    308    aRadicalRuleThickness = mathFont->MathTable()->Constant(
    309        gfxMathTable::RadicalRuleThickness, oneDevPixel);
    310  } else {
    311    GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
    312  }
    313 
    314  // get the leading to be left at the top of the resulting frame
    315  if (mathFont) {
    316    aRadicalExtraAscender = mathFont->MathTable()->Constant(
    317        gfxMathTable::RadicalExtraAscender, oneDevPixel);
    318  } else {
    319    // This seems more reliable than using aFontMetrics->GetLeading() on
    320    // suspicious fonts.
    321    nscoord em;
    322    GetEmHeight(aFontMetrics, em);
    323    aRadicalExtraAscender = nscoord(0.2f * em);
    324  }
    325 
    326  // get the clearance between rule and content
    327  if (mathFont) {
    328    aRadicalVerticalGap = mathFont->MathTable()->Constant(
    329        aDisplayStyle ? gfxMathTable::RadicalDisplayStyleVerticalGap
    330                      : gfxMathTable::RadicalVerticalGap,
    331        oneDevPixel);
    332  } else {
    333    // Rule 11, App. G, TeXbook
    334    aRadicalVerticalGap =
    335        aRadicalRuleThickness +
    336        (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;
    337  }
    338 }