layout/mathml/nsMathMLFrame.cpp

changeset 0
6474c204b198
     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 +}

mercurial