Bug 442637 - Apply ssty font feature setting to appropriate MathML elements. r=roc
authorJames Kitchener <jkitch.bug@gmail.com>
Wed, 15 Jan 2014 09:49:20 -0500
changeset 163590 10b9624cde2b998f9083dfd736d1cf1968b21154
parent 163589 a0b1c4cf207a6ee001773c6d44120f467a394503
child 163591 b595ecfe81171d3e8535b82034a71c7517b50fe2
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs442637
milestone29.0a1
Bug 442637 - Apply ssty font feature setting to appropriate MathML elements. r=roc
layout/generic/MathMLTextRunFactory.cpp
layout/generic/MathMLTextRunFactory.h
layout/generic/MathVariantTextRunFactory.cpp
layout/generic/MathVariantTextRunFactory.h
layout/generic/moz.build
layout/generic/nsIFrame.h
layout/generic/nsTextFrame.cpp
rename from layout/generic/MathVariantTextRunFactory.cpp
rename to layout/generic/MathMLTextRunFactory.cpp
--- a/layout/generic/MathVariantTextRunFactory.cpp
+++ b/layout/generic/MathMLTextRunFactory.cpp
@@ -1,14 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "MathVariantTextRunFactory.h"
+#include "MathMLTextRunFactory.h"
 
 #include "mozilla/ArrayUtils.h"
  
 #include "nsStyleConsts.h"
 #include "nsStyleContext.h"
 #include "nsTextFrameUtils.h"
 
 using namespace mozilla;
@@ -512,18 +512,18 @@ MathVariant(uint32_t aCh, uint8_t aMathV
   } else {
     // An Arabic character without a corresponding mapping
     return aCh;
   }
 
 }
 
 void
-nsMathVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
-                                            gfxContext* aRefContext)
+MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
+                                     gfxContext* aRefContext)
 {
   gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
   gfxFontStyle fontStyle = *fontGroup->GetStyle();
 
   nsAutoString convertedString;
   nsAutoTArray<bool,50> charsToMergeArray;
   nsAutoTArray<bool,50> deletedCharsArray;
   nsAutoTArray<nsStyleContext*,50> styleArray;
@@ -532,16 +532,66 @@ nsMathVariantTextRunFactory::RebuildText
 
   bool singleCharMI =
     aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI;
 
   uint32_t length = aTextRun->GetLength();
   const char16_t* str = aTextRun->mString.BeginReading();
   nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
 
+  if (mSSTYScriptLevel && length) {
+    bool found = false;
+    // We respect ssty settings explicitly set by the user
+    for (uint32_t i = 0; i < fontStyle.featureSettings.Length(); i++) {
+      if (fontStyle.featureSettings[i].mTag == TRUETYPE_TAG('s','s','t','y')) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      uint8_t sstyLevel = 0;
+      float scriptScaling = pow(styles[0]->StyleFont()->mScriptSizeMultiplier,
+                                mSSTYScriptLevel);
+      static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1,
+                    "Shouldn't it make things smaller?");
+      /*
+        An SSTY level of 2 is set if the scaling factor is less than or equal
+        to halfway between that for a scriptlevel of 1 (0.71) and that of a
+        scriptlevel of 2 (0.71^2), assuming the default script size multiplier.
+        An SSTY level of 1 is set if the script scaling factor is less than 
+        or equal that for a scriptlevel of 1 assuming the default script size
+        multiplier.
+
+        User specified values of script size multiplier will change the scaling
+        factor which mSSTYScriptLevel values correspond to.
+
+        In the event that the script size multiplier actually makes things
+        larger, no change is made.
+
+        If the user doesn't want this to happen, all they need to do is set
+        style="-moz-font-feature-settings: 'ssty' 0"
+      */
+      if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER +
+                            (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER*
+                             NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) {
+        // Currently only the first two ssty settings are used, so two is large
+        // as we go
+        sstyLevel = 2;
+      } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) {
+        sstyLevel = 1;
+      }
+      if (sstyLevel) {
+        gfxFontFeature settingSSTY;
+        settingSSTY.mTag = TRUETYPE_TAG('s','s','t','y');
+        settingSSTY.mValue = sstyLevel;
+        fontStyle.featureSettings.AppendElement(settingSSTY);
+      }
+    }
+  }
+
   uint8_t mathVar;
   bool doMathvariantStyling = true;
 
   for (uint32_t i = 0; i < length; ++i) {
     int extraChars = 0;
     nsStyleContext* styleContext = styles[i];
     mathVar = styleContext->StyleFont()->mMathVariant;
 
@@ -608,18 +658,20 @@ nsMathVariantTextRunFactory::RebuildText
     fontStyle.weight = NS_FONT_WEIGHT_BOLD;
   } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) {
     fontStyle.style = NS_FONT_STYLE_ITALIC;
     fontStyle.weight = NS_FONT_WEIGHT_NORMAL;
   } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC &&
              doMathvariantStyling) {
     fontStyle.style = NS_FONT_STYLE_ITALIC;
     fontStyle.weight = NS_FONT_WEIGHT_BOLD;
-  } else {
+  } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) {
     // Mathvariant overrides fontstyle and fontweight
+    // Need to check to see if mathvariant is actually applied as this function
+    // is used for other purposes.
     fontStyle.style = NS_FONT_STYLE_NORMAL;
     fontStyle.weight = NS_FONT_WEIGHT_NORMAL;
   }
   nsRefPtr<gfxFontGroup> newFontGroup = fontGroup->Copy(&fontStyle);
 
   if (!newFontGroup)
     return;
 
rename from layout/generic/MathVariantTextRunFactory.h
rename to layout/generic/MathMLTextRunFactory.h
--- a/layout/generic/MathVariantTextRunFactory.h
+++ b/layout/generic/MathMLTextRunFactory.h
@@ -1,25 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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/. */
 
-#ifndef MATHVARIANTTEXTRUNFACTORY_H_
-#define MATHVARIANTTEXTRUNFACTORY_H_
+#ifndef MATHMLTEXTRUNFACTORY_H_
+#define MATHMLTEXTRUNFACTORY_H_
 
 #include "nsTextRunTransformations.h"
 
 /**
- * Builds textruns that render their text using a mathvariant
+ * Builds textruns that render their text with MathML specific renderings.
  */
-class nsMathVariantTextRunFactory : public nsTransformingTextRunFactory {
+class MathMLTextRunFactory : public nsTransformingTextRunFactory {
 public:
-  nsMathVariantTextRunFactory(nsTransformingTextRunFactory* aInnerTransformingTextRunFactory)
-    : mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory) {}
+  MathMLTextRunFactory(nsTransformingTextRunFactory* aInnerTransformingTextRunFactory,
+                       uint8_t aSSTYScriptLevel)
+    : mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory),
+      mSSTYScriptLevel(aSSTYScriptLevel) {}
 
   virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
                               gfxContext* aRefContext) MOZ_OVERRIDE;
 protected:
   nsAutoPtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
+  uint8_t mSSTYScriptLevel;
 };
 
-#endif /*MATHVARIANTTEXTRUNFACTORY_H_*/
+#endif /*MATHMLTEXTRUNFACTORY_H_*/
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -39,17 +39,17 @@ EXPORTS.mozilla += [
 ]
 
 EXPORTS.mozilla.layout += [
     'FrameChildList.h',
 ]
 
 UNIFIED_SOURCES += [
     'FrameChildList.cpp',
-    'MathVariantTextRunFactory.cpp',
+    'MathMLTextRunFactory.cpp',
     'nsAbsoluteContainingBlock.cpp',
     'nsBlockFrame.cpp',
     'nsBlockReflowContext.cpp',
     'nsBlockReflowState.cpp',
     'nsBRFrame.cpp',
     'nsBulletFrame.cpp',
     'nsCanvasFrame.cpp',
     'nsColumnSetFrame.cpp',
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -333,16 +333,20 @@ typedef uint64_t nsFrameState;
 
 // Frame is not displayed directly due to it being, or being under, an SVG
 // <defs> element or an SVG resource element (<mask>, <pattern>, etc.)
 #define NS_FRAME_IS_NONDISPLAY                      NS_FRAME_STATE_BIT(53)
 
 // Frame has a LayerActivityProperty property
 #define NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY        NS_FRAME_STATE_BIT(54)
 
+// Set for all descendants of MathML sub/supscript elements (other than the
+// base frame) to indicate that the SSTY font feature should be used.
+#define NS_FRAME_MATHML_SCRIPT_DESCENDANT           NS_FRAME_STATE_BIT(58)
+
 // Box layout bits
 #define NS_STATE_IS_HORIZONTAL                      NS_FRAME_STATE_BIT(22)
 #define NS_STATE_IS_DIRECTION_NORMAL                NS_FRAME_STATE_BIT(31)
 
 // Helper macros
 #define NS_SUBTREE_DIRTY(_frame)  \
   (((_frame)->GetStateBits() &      \
     (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0)
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -33,20 +33,21 @@
 #include "nsTArray.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCompatibility.h"
 #include "nsCSSColorUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsFrame.h"
+#include "nsIMathMLFrame.h"
 #include "nsPlaceholderFrame.h"
 #include "nsTextFrameUtils.h"
 #include "nsTextRunTransformations.h"
-#include "MathVariantTextRunFactory.h"
+#include "MathMLTextRunFactory.h"
 #include "nsExpirationTracker.h"
 #include "nsUnicodeProperties.h"
 
 #include "nsTextFragment.h"
 #include "nsGkAtoms.h"
 #include "nsFrameSelection.h"
 #include "nsRange.h"
 #include "nsCSSRendering.h"
@@ -1324,16 +1325,19 @@ BuildTextRuns(gfxContext* aContext, nsTe
                   (aLineContainer->GetType() == nsGkAtoms::letterFrame &&
                    aLineContainer->IsFloating())),
                  "Wrong line container hint");
   }
 
   if (aForFrame && aForFrame->HasAnyStateBits(TEXT_IS_IN_SINGLE_CHAR_MI)) {
     aLineContainer->AddStateBits(TEXT_IS_IN_SINGLE_CHAR_MI);
   }
+  if (aForFrame && aForFrame->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
+    aLineContainer->AddStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT);
+  }
 
   nsPresContext* presContext = aLineContainer->PresContext();
   BuildTextRunsScanner scanner(presContext, aContext, aLineContainer,
                                aWhichTextRun);
 
   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aLineContainer);
 
   if (!block) {
@@ -1888,17 +1892,18 @@ static const nsTextFrameUtils::Compressi
 gfxTextRun*
 BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
 {
   gfxSkipChars skipChars;
 
   const void* textPtr = aTextBuffer;
   bool anySmallcapsStyle = false;
   bool anyTextTransformStyle = false;
-  bool anyMathVariantStyle = false;
+  bool anyMathMLStyling = false;
+  uint8_t sstyScriptLevel = 0;
   uint32_t textFlags = nsTextFrameUtils::TEXT_NO_BREAKS;
 
   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
     textFlags |= nsTextFrameUtils::TEXT_INCOMING_WHITESPACE;
   }
   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
     textFlags |= gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR;
   }
@@ -1967,20 +1972,45 @@ BuildTextRunsScanner::BuildTextRunForFra
     if (enabledJustification && !textStyle->WhiteSpaceIsSignificant()) {
       textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
     }
     fontStyle = f->StyleFont();
     if (NS_STYLE_FONT_VARIANT_SMALL_CAPS == fontStyle->mFont.variant) {
       anySmallcapsStyle = true;
     }
     if (NS_MATHML_MATHVARIANT_NONE != fontStyle->mMathVariant) {
-      anyMathVariantStyle = true;
+      anyMathMLStyling = true;
     } else if (mLineContainer->GetStateBits() & TEXT_IS_IN_SINGLE_CHAR_MI) {
       textFlags |= nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI;
-      anyMathVariantStyle = true;
+      anyMathMLStyling = true;
+    }
+    nsIFrame* parent = mLineContainer->GetParent();
+    nsIFrame* child = mLineContainer;
+    uint8_t oldScriptLevel = 0;
+    while (parent && 
+           child->HasAnyStateBits(NS_FRAME_MATHML_SCRIPT_DESCENDANT)) {
+      // Reconstruct the script level ignoring any user overrides. It is
+      // calculated this way instead of using scriptlevel to ensure the 
+      // correct ssty font feature setting is used even if the user sets a
+      // different (especially negative) scriptlevel.
+      nsIMathMLFrame* mathFrame= do_QueryFrame(parent);
+      if (mathFrame) {
+        sstyScriptLevel += mathFrame->ScriptIncrement(child);
+      }
+      if (sstyScriptLevel < oldScriptLevel) {
+        // overflow
+        sstyScriptLevel = UINT8_MAX;
+        break;
+      }
+      child = parent;
+      parent = parent->GetParent();
+      oldScriptLevel = sstyScriptLevel;
+    }
+    if (sstyScriptLevel) {
+      anyMathMLStyling = true;
     }
 
     // Figure out what content is included in this flow.
     nsIContent* content = f->GetContent();
     const nsTextFragment* frag = content->GetText();
     int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
     int32_t contentEnd = mappedFlow->GetContentEnd();
     int32_t contentLength = contentEnd - contentStart;
@@ -2105,19 +2135,19 @@ BuildTextRunsScanner::BuildTextRunForFra
   nsAutoPtr<nsTransformingTextRunFactory> transformingFactory;
   if (anySmallcapsStyle) {
     transformingFactory = new nsFontVariantTextRunFactory();
   }
   if (anyTextTransformStyle) {
     transformingFactory =
       new nsCaseTransformTextRunFactory(transformingFactory.forget());
   }
-  if (anyMathVariantStyle) {
+  if (anyMathMLStyling) {
     transformingFactory =
-      new nsMathVariantTextRunFactory(transformingFactory.forget());
+      new MathMLTextRunFactory(transformingFactory.forget(), sstyScriptLevel);
   }
   nsTArray<nsStyleContext*> styles;
   if (transformingFactory) {
     iter.SetOriginalOffset(0);
     for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
       MappedFlow* mappedFlow = &mMappedFlows[i];
       nsTextFrame* f;
       for (f = mappedFlow->mStartFrame; f != mappedFlow->mEndFrame;