nsMathMLFrame.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsMathMLFrame.h"
  6. #include "gfxUtils.h"
  7. #include "mozilla/gfx/2D.h"
  8. #include "nsLayoutUtils.h"
  9. #include "nsNameSpaceManager.h"
  10. #include "nsMathMLChar.h"
  11. #include "nsCSSPseudoElements.h"
  12. #include "nsMathMLElement.h"
  13. #include "gfxMathTable.h"
  14. // used to map attributes into CSS rules
  15. #include "mozilla/StyleSetHandle.h"
  16. #include "mozilla/StyleSetHandleInlines.h"
  17. #include "nsDisplayList.h"
  18. #include "nsRenderingContext.h"
  19. using namespace mozilla;
  20. using namespace mozilla::gfx;
  21. eMathMLFrameType
  22. nsMathMLFrame::GetMathMLFrameType()
  23. {
  24. // see if it is an embellished operator (mapped to 'Op' in TeX)
  25. if (mEmbellishData.coreFrame)
  26. return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
  27. // if it has a prescribed base, fetch the type from there
  28. if (mPresentationData.baseFrame)
  29. return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
  30. // everything else is treated as ordinary (mapped to 'Ord' in TeX)
  31. return eMathMLFrameType_Ordinary;
  32. }
  33. NS_IMETHODIMP
  34. nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent)
  35. {
  36. mEmbellishData.flags = 0;
  37. mEmbellishData.coreFrame = nullptr;
  38. mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
  39. mEmbellishData.leadingSpace = 0;
  40. mEmbellishData.trailingSpace = 0;
  41. mPresentationData.flags = 0;
  42. mPresentationData.baseFrame = nullptr;
  43. // by default, just inherit the display of our parent
  44. nsPresentationData parentData;
  45. GetPresentationDataFrom(aParent, parentData);
  46. #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
  47. mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
  48. #endif
  49. return NS_OK;
  50. }
  51. NS_IMETHODIMP
  52. nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues,
  53. uint32_t aWhichFlags)
  54. {
  55. NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags) ||
  56. NS_MATHML_IS_DTLS_SET(aWhichFlags),
  57. "aWhichFlags should only be compression or dtls flag");
  58. if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) {
  59. // updating the compression flag is allowed
  60. if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
  61. // 'compressed' means 'prime' style in App. G, TeXbook
  62. mPresentationData.flags |= NS_MATHML_COMPRESSED;
  63. }
  64. // no else. the flag is sticky. it retains its value once it is set
  65. }
  66. // These flags determine whether the dtls font feature settings should
  67. // be applied.
  68. if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
  69. if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
  70. mPresentationData.flags |= NS_MATHML_DTLS;
  71. } else {
  72. mPresentationData.flags &= ~NS_MATHML_DTLS;
  73. }
  74. }
  75. return NS_OK;
  76. }
  77. // Helper to give a style context suitable for doing the stretching of
  78. // a MathMLChar. Frame classes that use this should ensure that the
  79. // extra leaf style contexts given to the MathMLChars are accessible to
  80. // the Style System via the Get/Set AdditionalStyleContext() APIs.
  81. /* static */ void
  82. nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext,
  83. nsIContent* aContent,
  84. nsStyleContext* aParentStyleContext,
  85. nsMathMLChar* aMathMLChar)
  86. {
  87. CSSPseudoElementType pseudoType =
  88. CSSPseudoElementType::mozMathAnonymous; // savings
  89. RefPtr<nsStyleContext> newStyleContext;
  90. newStyleContext = aPresContext->StyleSet()->
  91. ResolvePseudoElementStyle(aContent->AsElement(), pseudoType,
  92. aParentStyleContext, nullptr);
  93. aMathMLChar->SetStyleContext(newStyleContext);
  94. }
  95. /* static */ void
  96. nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame,
  97. nsEmbellishData& aEmbellishData)
  98. {
  99. // initialize OUT params
  100. aEmbellishData.flags = 0;
  101. aEmbellishData.coreFrame = nullptr;
  102. aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
  103. aEmbellishData.leadingSpace = 0;
  104. aEmbellishData.trailingSpace = 0;
  105. if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
  106. nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
  107. if (mathMLFrame) {
  108. mathMLFrame->GetEmbellishData(aEmbellishData);
  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 */ void
  115. nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame,
  116. nsPresentationData& aPresentationData,
  117. bool aClimbTree)
  118. {
  119. // initialize OUT params
  120. aPresentationData.flags = 0;
  121. aPresentationData.baseFrame = nullptr;
  122. nsIFrame* frame = aFrame;
  123. while (frame) {
  124. if (frame->IsFrameOfType(nsIFrame::eMathML)) {
  125. nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
  126. if (mathMLFrame) {
  127. mathMLFrame->GetPresentationData(aPresentationData);
  128. break;
  129. }
  130. }
  131. // stop if the caller doesn't want to lookup beyond the frame
  132. if (!aClimbTree) {
  133. break;
  134. }
  135. // stop if we reach the root <math> tag
  136. nsIContent* content = frame->GetContent();
  137. NS_ASSERTION(content || !frame->GetParent(), // no assert for the root
  138. "dangling frame without a content node");
  139. if (!content)
  140. break;
  141. if (content->IsMathMLElement(nsGkAtoms::math)) {
  142. break;
  143. }
  144. frame = frame->GetParent();
  145. }
  146. NS_WARNING_ASSERTION(
  147. frame && frame->GetContent(),
  148. "bad MathML markup - could not find the top <math> element");
  149. }
  150. /* static */ void
  151. nsMathMLFrame::GetRuleThickness(DrawTarget* aDrawTarget,
  152. nsFontMetrics* aFontMetrics,
  153. nscoord& aRuleThickness)
  154. {
  155. nscoord xHeight = aFontMetrics->XHeight();
  156. char16_t overBar = 0x00AF;
  157. nsBoundingMetrics bm =
  158. nsLayoutUtils::AppUnitBoundsOfString(&overBar, 1, *aFontMetrics,
  159. 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. /* static */ void
  167. nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
  168. nsFontMetrics* aFontMetrics,
  169. nscoord& aAxisHeight)
  170. {
  171. gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
  172. if (mathFont) {
  173. aAxisHeight =
  174. mathFont->MathTable()->Constant(gfxMathTable::AxisHeight,
  175. aFontMetrics->AppUnitsPerDevPixel());
  176. return;
  177. }
  178. nscoord xHeight = aFontMetrics->XHeight();
  179. char16_t minus = 0x2212; // not '-', but official Unicode minus sign
  180. nsBoundingMetrics bm =
  181. nsLayoutUtils::AppUnitBoundsOfString(&minus, 1, *aFontMetrics, aDrawTarget);
  182. aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2;
  183. if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
  184. // fall-back to the other version
  185. GetAxisHeight(aFontMetrics, aAxisHeight);
  186. }
  187. }
  188. /* static */ nscoord
  189. nsMathMLFrame::CalcLength(nsPresContext* aPresContext,
  190. nsStyleContext* aStyleContext,
  191. const nsCSSValue& aCSSValue,
  192. float aFontSizeInflation)
  193. {
  194. NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
  195. if (aCSSValue.IsFixedLengthUnit()) {
  196. return aCSSValue.GetFixedLength(aPresContext);
  197. }
  198. if (aCSSValue.IsPixelLengthUnit()) {
  199. return aCSSValue.GetPixelLength();
  200. }
  201. nsCSSUnit unit = aCSSValue.GetUnit();
  202. if (eCSSUnit_EM == unit) {
  203. const nsStyleFont* font = aStyleContext->StyleFont();
  204. return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size);
  205. }
  206. else if (eCSSUnit_XHeight == unit) {
  207. aPresContext->SetUsesExChUnits(true);
  208. RefPtr<nsFontMetrics> fm = nsLayoutUtils::
  209. GetFontMetricsForStyleContext(aStyleContext, aFontSizeInflation);
  210. nscoord xHeight = fm->XHeight();
  211. return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
  212. }
  213. // MathML doesn't specify other CSS units such as rem or ch
  214. NS_ERROR("Unsupported unit");
  215. return 0;
  216. }
  217. /* static */ void
  218. nsMathMLFrame::ParseNumericValue(const nsString& aString,
  219. nscoord* aLengthValue,
  220. uint32_t aFlags,
  221. nsPresContext* aPresContext,
  222. nsStyleContext* aStyleContext,
  223. float aFontSizeInflation)
  224. {
  225. nsCSSValue cssValue;
  226. if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags,
  227. aPresContext->Document())) {
  228. // Invalid attribute value. aLengthValue remains unchanged, so the default
  229. // length value is used.
  230. return;
  231. }
  232. nsCSSUnit unit = cssValue.GetUnit();
  233. if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
  234. // Relative units. A multiple of the default length value is used.
  235. *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ?
  236. cssValue.GetPercentValue() :
  237. cssValue.GetFloatValue()));
  238. return;
  239. }
  240. // Absolute units.
  241. *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue,
  242. aFontSizeInflation);
  243. }
  244. // ================
  245. // Utils to map attributes into CSS rules (work-around to bug 69409 which
  246. // is not scheduled to be fixed anytime soon)
  247. //
  248. struct
  249. nsCSSMapping {
  250. int32_t compatibility;
  251. const nsIAtom* attrAtom;
  252. const char* cssProperty;
  253. };
  254. #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
  255. class nsDisplayMathMLBoundingMetrics : public nsDisplayItem {
  256. public:
  257. nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder,
  258. nsIFrame* aFrame, const nsRect& aRect)
  259. : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
  260. MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics);
  261. }
  262. #ifdef NS_BUILD_REFCNT_LOGGING
  263. virtual ~nsDisplayMathMLBoundingMetrics() {
  264. MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics);
  265. }
  266. #endif
  267. virtual void Paint(nsDisplayListBuilder* aBuilder,
  268. nsRenderingContext* aCtx) override;
  269. NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
  270. private:
  271. nsRect mRect;
  272. };
  273. void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
  274. nsRenderingContext* aCtx)
  275. {
  276. DrawTarget* drawTarget = aCtx->GetDrawTarget();
  277. Rect r = NSRectToRect(mRect + ToReferenceFrame(),
  278. mFrame->PresContext()->AppUnitsPerDevPixel());
  279. ColorPattern blue(ToDeviceColor(Color(0.f, 0.f, 1.f, 1.f)));
  280. drawTarget->StrokeRect(r, blue);
  281. }
  282. void
  283. nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder,
  284. nsIFrame* aFrame, const nsPoint& aPt,
  285. const nsBoundingMetrics& aMetrics,
  286. const nsDisplayListSet& aLists) {
  287. if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
  288. return;
  289. nscoord x = aPt.x + aMetrics.leftBearing;
  290. nscoord y = aPt.y - aMetrics.ascent;
  291. nscoord w = aMetrics.rightBearing - aMetrics.leftBearing;
  292. nscoord h = aMetrics.ascent + aMetrics.descent;
  293. aLists.Content()->AppendNewToTop(new (aBuilder)
  294. nsDisplayMathMLBoundingMetrics(aBuilder, aFrame, nsRect(x,y,w,h)));
  295. }
  296. #endif
  297. class nsDisplayMathMLBar : public nsDisplayItem {
  298. public:
  299. nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder,
  300. nsIFrame* aFrame, const nsRect& aRect)
  301. : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
  302. MOZ_COUNT_CTOR(nsDisplayMathMLBar);
  303. }
  304. #ifdef NS_BUILD_REFCNT_LOGGING
  305. virtual ~nsDisplayMathMLBar() {
  306. MOZ_COUNT_DTOR(nsDisplayMathMLBar);
  307. }
  308. #endif
  309. virtual void Paint(nsDisplayListBuilder* aBuilder,
  310. nsRenderingContext* aCtx) override;
  311. NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
  312. private:
  313. nsRect mRect;
  314. };
  315. void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
  316. nsRenderingContext* aCtx)
  317. {
  318. // paint the bar with the current text color
  319. DrawTarget* drawTarget = aCtx->GetDrawTarget();
  320. Rect rect =
  321. NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
  322. mFrame->PresContext()->AppUnitsPerDevPixel(),
  323. *drawTarget);
  324. ColorPattern color(ToDeviceColor(
  325. mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
  326. drawTarget->FillRect(rect, color);
  327. }
  328. void
  329. nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
  330. nsIFrame* aFrame, const nsRect& aRect,
  331. const nsDisplayListSet& aLists) {
  332. if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
  333. return;
  334. aLists.Content()->AppendNewToTop(new (aBuilder)
  335. nsDisplayMathMLBar(aBuilder, aFrame, aRect));
  336. }
  337. void
  338. nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
  339. bool aDisplayStyle,
  340. nscoord& aRadicalRuleThickness,
  341. nscoord& aRadicalExtraAscender,
  342. nscoord& aRadicalVerticalGap)
  343. {
  344. nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
  345. gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
  346. // get the radical rulethickness
  347. if (mathFont) {
  348. aRadicalRuleThickness = mathFont->MathTable()->
  349. Constant(gfxMathTable::RadicalRuleThickness, oneDevPixel);
  350. } else {
  351. GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
  352. }
  353. // get the leading to be left at the top of the resulting frame
  354. if (mathFont) {
  355. aRadicalExtraAscender = mathFont->MathTable()->
  356. Constant(gfxMathTable::RadicalExtraAscender, oneDevPixel);
  357. } else {
  358. // This seems more reliable than using aFontMetrics->GetLeading() on
  359. // suspicious fonts.
  360. nscoord em;
  361. GetEmHeight(aFontMetrics, em);
  362. aRadicalExtraAscender = nscoord(0.2f * em);
  363. }
  364. // get the clearance between rule and content
  365. if (mathFont) {
  366. aRadicalVerticalGap = mathFont->MathTable()->
  367. Constant(aDisplayStyle ?
  368. gfxMathTable::RadicalDisplayStyleVerticalGap :
  369. gfxMathTable::RadicalVerticalGap,
  370. oneDevPixel);
  371. } else {
  372. // Rule 11, App. G, TeXbook
  373. aRadicalVerticalGap = aRadicalRuleThickness +
  374. (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;
  375. }
  376. }