PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/layout/mathml/nsMathMLmpaddedFrame.cpp

https://bitbucket.org/soko/mozilla-central
C++ | 490 lines | 283 code | 68 blank | 139 comment | 78 complexity | 9ba73b4fd0448faa8c7af4604924ca27 MD5 | raw file
Possible License(s): GPL-2.0, JSON, 0BSD, LGPL-3.0, AGPL-1.0, MIT, MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.1, Apache-2.0
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is Mozilla MathML Project.
  16. *
  17. * The Initial Developer of the Original Code is
  18. * The University Of Queensland.
  19. * Portions created by the Initial Developer are Copyright (C) 1999
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Roger B. Sidje <rbs@maths.uq.edu.au>
  24. * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
  25. * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. #include "nsCOMPtr.h"
  41. #include "nsCRT.h" // to get NS_IS_SPACE
  42. #include "nsFrame.h"
  43. #include "nsPresContext.h"
  44. #include "nsStyleContext.h"
  45. #include "nsStyleConsts.h"
  46. #include "nsMathMLmpaddedFrame.h"
  47. //
  48. // <mpadded> -- adjust space around content - implementation
  49. //
  50. #define NS_MATHML_SIGN_INVALID -1 // if the attribute is not there
  51. #define NS_MATHML_SIGN_UNSPECIFIED 0
  52. #define NS_MATHML_SIGN_MINUS 1
  53. #define NS_MATHML_SIGN_PLUS 2
  54. #define NS_MATHML_PSEUDO_UNIT_UNSPECIFIED 0
  55. #define NS_MATHML_PSEUDO_UNIT_ITSELF 1 // special
  56. #define NS_MATHML_PSEUDO_UNIT_WIDTH 2
  57. #define NS_MATHML_PSEUDO_UNIT_HEIGHT 3
  58. #define NS_MATHML_PSEUDO_UNIT_DEPTH 4
  59. #define NS_MATHML_PSEUDO_UNIT_NAMEDSPACE 5
  60. nsIFrame*
  61. NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  62. {
  63. return new (aPresShell) nsMathMLmpaddedFrame(aContext);
  64. }
  65. NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmpaddedFrame)
  66. nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame()
  67. {
  68. }
  69. NS_IMETHODIMP
  70. nsMathMLmpaddedFrame::InheritAutomaticData(nsIFrame* aParent)
  71. {
  72. // let the base class get the default from our parent
  73. nsMathMLContainerFrame::InheritAutomaticData(aParent);
  74. mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
  75. return NS_OK;
  76. }
  77. void
  78. nsMathMLmpaddedFrame::ProcessAttributes()
  79. {
  80. /*
  81. parse the attributes
  82. width = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
  83. height = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
  84. depth = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
  85. lspace = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
  86. voffset= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit | namedspace)
  87. */
  88. nsAutoString value;
  89. /* The REC says:
  90. There is one exceptional element, <mpadded>, whose attributes cannot be
  91. set with <mstyle>. When the attributes width, height and depth are specified
  92. on an <mstyle> element, they apply only to the <mspace/> element. Similarly,
  93. when lspace is set with <mstyle>, it applies only to the <mo> element.
  94. */
  95. // See if attributes are local, don't access mstyle !
  96. // width
  97. mWidthSign = NS_MATHML_SIGN_INVALID;
  98. GetAttribute(mContent, nsnull, nsGkAtoms::width, value);
  99. if (!value.IsEmpty()) {
  100. ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit);
  101. }
  102. // height
  103. mHeightSign = NS_MATHML_SIGN_INVALID;
  104. GetAttribute(mContent, nsnull, nsGkAtoms::height, value);
  105. if (!value.IsEmpty()) {
  106. ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit);
  107. }
  108. // depth
  109. mDepthSign = NS_MATHML_SIGN_INVALID;
  110. GetAttribute(mContent, nsnull, nsGkAtoms::depth_, value);
  111. if (!value.IsEmpty()) {
  112. ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit);
  113. }
  114. // lspace
  115. mLeadingSpaceSign = NS_MATHML_SIGN_INVALID;
  116. GetAttribute(mContent, nsnull, nsGkAtoms::lspace_, value);
  117. if (!value.IsEmpty()) {
  118. ParseAttribute(value, mLeadingSpaceSign, mLeadingSpace,
  119. mLeadingSpacePseudoUnit);
  120. }
  121. // voffset
  122. mVerticalOffsetSign = NS_MATHML_SIGN_INVALID;
  123. GetAttribute(mContent, nsnull, nsGkAtoms::voffset_, value);
  124. if (!value.IsEmpty()) {
  125. ParseAttribute(value, mVerticalOffsetSign, mVerticalOffset,
  126. mVerticalOffsetPseudoUnit);
  127. }
  128. }
  129. // parse an input string in the following format (see bug 148326 for testcases):
  130. // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
  131. bool
  132. nsMathMLmpaddedFrame::ParseAttribute(nsString& aString,
  133. PRInt32& aSign,
  134. nsCSSValue& aCSSValue,
  135. PRInt32& aPseudoUnit)
  136. {
  137. aCSSValue.Reset();
  138. aSign = NS_MATHML_SIGN_INVALID;
  139. aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED;
  140. aString.CompressWhitespace(); // aString is not a const in this code
  141. PRInt32 stringLength = aString.Length();
  142. if (!stringLength)
  143. return false;
  144. nsAutoString number, unit;
  145. //////////////////////
  146. // see if the sign is there
  147. PRInt32 i = 0;
  148. if (aString[0] == '+') {
  149. aSign = NS_MATHML_SIGN_PLUS;
  150. i++;
  151. }
  152. else if (aString[0] == '-') {
  153. aSign = NS_MATHML_SIGN_MINUS;
  154. i++;
  155. }
  156. else
  157. aSign = NS_MATHML_SIGN_UNSPECIFIED;
  158. // skip any space after the sign
  159. if (i < stringLength && nsCRT::IsAsciiSpace(aString[i]))
  160. i++;
  161. // get the number
  162. bool gotDot = false, gotPercent = false;
  163. for (; i < stringLength; i++) {
  164. PRUnichar c = aString[i];
  165. if (gotDot && c == '.') {
  166. // error - two dots encountered
  167. aSign = NS_MATHML_SIGN_INVALID;
  168. return false;
  169. }
  170. if (c == '.')
  171. gotDot = true;
  172. else if (!nsCRT::IsAsciiDigit(c)) {
  173. break;
  174. }
  175. number.Append(c);
  176. }
  177. // catch error if we didn't enter the loop above... we could simply initialize
  178. // floatValue = 1, to cater for cases such as width="height", but that wouldn't
  179. // be in line with the spec which requires an explicit number
  180. if (number.IsEmpty()) {
  181. #ifdef NS_DEBUG
  182. printf("mpadded: attribute with bad numeric value: %s\n",
  183. NS_LossyConvertUTF16toASCII(aString).get());
  184. #endif
  185. aSign = NS_MATHML_SIGN_INVALID;
  186. return false;
  187. }
  188. PRInt32 errorCode;
  189. float floatValue = number.ToFloat(&errorCode);
  190. if (errorCode) {
  191. aSign = NS_MATHML_SIGN_INVALID;
  192. return false;
  193. }
  194. // skip any space after the number
  195. if (i < stringLength && nsCRT::IsAsciiSpace(aString[i]))
  196. i++;
  197. // see if this is a percentage-based value
  198. if (i < stringLength && aString[i] == '%') {
  199. i++;
  200. gotPercent = true;
  201. // skip any space after the '%' sign
  202. if (i < stringLength && nsCRT::IsAsciiSpace(aString[i]))
  203. i++;
  204. }
  205. // the remainder now should be a css-unit, or a pseudo-unit, or a named-space
  206. aString.Right(unit, stringLength - i);
  207. if (unit.IsEmpty()) {
  208. // also cater for the edge case of "0" for which the unit is optional
  209. if (gotPercent || !floatValue) {
  210. aCSSValue.SetPercentValue(floatValue / 100.0f);
  211. aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
  212. return true;
  213. }
  214. /*
  215. else {
  216. // no explicit CSS unit and no explicit pseudo-unit...
  217. // In this case, the MathML REC suggests taking ems for
  218. // h-unit (width, lspace) or exs for v-unit (height, depth).
  219. // Here, however, we explicitly request authors to specify
  220. // the unit. This is more in line with the CSS REC (and
  221. // it allows keeping the code simpler...)
  222. }
  223. */
  224. }
  225. else if (unit.EqualsLiteral("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH;
  226. else if (unit.EqualsLiteral("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT;
  227. else if (unit.EqualsLiteral("depth")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH;
  228. else if (!gotPercent) { // percentage can only apply to a pseudo-unit
  229. // see if the unit is a named-space
  230. // XXX nsnull in ParseNamedSpacedValue()? don't access mstyle?
  231. if (ParseNamedSpaceValue(nsnull, unit, aCSSValue)) {
  232. // re-scale properly, and we know that the unit of the named-space is 'em'
  233. floatValue *= aCSSValue.GetFloatValue();
  234. aCSSValue.SetFloatValue(floatValue, eCSSUnit_EM);
  235. aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE;
  236. return true;
  237. }
  238. // see if the input was just a CSS value
  239. number.Append(unit); // leave the sign out if it was there
  240. if (ParseNumericValue(number, aCSSValue))
  241. return true;
  242. }
  243. // if we enter here, we have a number that will act as a multiplier on a pseudo-unit
  244. if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) {
  245. if (gotPercent)
  246. aCSSValue.SetPercentValue(floatValue / 100.0f);
  247. else
  248. aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
  249. return true;
  250. }
  251. #ifdef NS_DEBUG
  252. printf("mpadded: attribute with bad numeric value: %s\n",
  253. NS_LossyConvertUTF16toASCII(aString).get());
  254. #endif
  255. // if we reach here, it means we encounter an unexpected input
  256. aSign = NS_MATHML_SIGN_INVALID;
  257. return false;
  258. }
  259. void
  260. nsMathMLmpaddedFrame::UpdateValue(PRInt32 aSign,
  261. PRInt32 aPseudoUnit,
  262. const nsCSSValue& aCSSValue,
  263. const nsBoundingMetrics& aBoundingMetrics,
  264. nscoord& aValueToUpdate) const
  265. {
  266. nsCSSUnit unit = aCSSValue.GetUnit();
  267. if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) {
  268. nscoord scaler = 0, amount = 0;
  269. if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) {
  270. switch(aPseudoUnit) {
  271. case NS_MATHML_PSEUDO_UNIT_WIDTH:
  272. scaler = aBoundingMetrics.width;
  273. break;
  274. case NS_MATHML_PSEUDO_UNIT_HEIGHT:
  275. scaler = aBoundingMetrics.ascent;
  276. break;
  277. case NS_MATHML_PSEUDO_UNIT_DEPTH:
  278. scaler = aBoundingMetrics.descent;
  279. break;
  280. default:
  281. // if we ever reach here, it would mean something is wrong
  282. // somewhere with the setup and/or the caller
  283. NS_ERROR("Unexpected Pseudo Unit");
  284. return;
  285. }
  286. }
  287. if (eCSSUnit_Number == unit)
  288. amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue());
  289. else if (eCSSUnit_Percent == unit)
  290. amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue());
  291. else
  292. amount = CalcLength(PresContext(), mStyleContext, aCSSValue);
  293. if (NS_MATHML_SIGN_PLUS == aSign)
  294. aValueToUpdate += amount;
  295. else if (NS_MATHML_SIGN_MINUS == aSign)
  296. aValueToUpdate -= amount;
  297. else
  298. aValueToUpdate = amount;
  299. }
  300. }
  301. NS_IMETHODIMP
  302. nsMathMLmpaddedFrame::Reflow(nsPresContext* aPresContext,
  303. nsHTMLReflowMetrics& aDesiredSize,
  304. const nsHTMLReflowState& aReflowState,
  305. nsReflowStatus& aStatus)
  306. {
  307. ProcessAttributes();
  308. ///////////////
  309. // Let the base class format our content like an inferred mrow
  310. nsresult rv = nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
  311. aReflowState, aStatus);
  312. //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
  313. return rv;
  314. }
  315. /* virtual */ nsresult
  316. nsMathMLmpaddedFrame::Place(nsRenderingContext& aRenderingContext,
  317. bool aPlaceOrigin,
  318. nsHTMLReflowMetrics& aDesiredSize)
  319. {
  320. nsresult rv =
  321. nsMathMLContainerFrame::Place(aRenderingContext, false, aDesiredSize);
  322. if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
  323. DidReflowChildren(GetFirstPrincipalChild());
  324. return rv;
  325. }
  326. nscoord height = mBoundingMetrics.ascent;
  327. nscoord depth = mBoundingMetrics.descent;
  328. // The REC says:
  329. //
  330. // "The lspace attribute ('leading' space) specifies the horizontal location
  331. // of the positioning point of the child content with respect to the
  332. // positioning point of the mpadded element. By default they coincide, and
  333. // therefore absolute values for lspace have the same effect as relative
  334. // values."
  335. //
  336. // "MathML renderers should ensure that, except for the effects of the
  337. // attributes, the relative spacing between the contents of the mpadded
  338. // element and surrounding MathML elements would not be modified by replacing
  339. // an mpadded element with an mrow element with the same content, even if
  340. // linebreaking occurs within the mpadded element."
  341. //
  342. // (http://www.w3.org/TR/MathML/chapter3.html#presm.mpadded)
  343. //
  344. // "In those discussions, the terms leading and trailing are used to specify
  345. // a side of an object when which side to use depends on the directionality;
  346. // ie. leading means left in LTR but right in RTL."
  347. // (http://www.w3.org/TR/MathML/chapter3.html#presm.bidi.math)
  348. nscoord lspace = 0;
  349. // In MathML3, "width" will be the bounding box width and "advancewidth" will
  350. // refer "to the horizontal distance between the positioning point of the
  351. // mpadded and the positioning point for the following content". MathML2
  352. // doesn't make the distinction.
  353. nscoord width = mBoundingMetrics.width;
  354. nscoord voffset = 0;
  355. PRInt32 pseudoUnit;
  356. nscoord initialWidth = width;
  357. // update width
  358. pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
  359. ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit;
  360. UpdateValue(mWidthSign, pseudoUnit, mWidth,
  361. mBoundingMetrics, width);
  362. width = NS_MAX(0, width);
  363. // update "height" (this is the ascent in the terminology of the REC)
  364. pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
  365. ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit;
  366. UpdateValue(mHeightSign, pseudoUnit, mHeight,
  367. mBoundingMetrics, height);
  368. height = NS_MAX(0, height);
  369. // update "depth" (this is the descent in the terminology of the REC)
  370. pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
  371. ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit;
  372. UpdateValue(mDepthSign, pseudoUnit, mDepth,
  373. mBoundingMetrics, depth);
  374. depth = NS_MAX(0, depth);
  375. // update lspace
  376. if (mLeadingSpacePseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
  377. pseudoUnit = mLeadingSpacePseudoUnit;
  378. UpdateValue(mLeadingSpaceSign, pseudoUnit, mLeadingSpace,
  379. mBoundingMetrics, lspace);
  380. }
  381. // update voffset
  382. if (mVerticalOffsetPseudoUnit != NS_MATHML_PSEUDO_UNIT_ITSELF) {
  383. pseudoUnit = mVerticalOffsetPseudoUnit;
  384. UpdateValue(mVerticalOffsetSign, pseudoUnit, mVerticalOffset,
  385. mBoundingMetrics, voffset);
  386. }
  387. // do the padding now that we have everything
  388. // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
  389. // with no attributes) looks the same as <mrow>...</mrow>. But when there are
  390. // attributes, tweak our metrics and move children to achieve the desired visual
  391. // effects.
  392. if ((NS_MATHML_IS_RTL(mPresentationData.flags) ?
  393. mWidthSign : mLeadingSpaceSign) != NS_MATHML_SIGN_INVALID) {
  394. // there was padding on the left. dismiss the left italic correction now
  395. // (so that our parent won't correct us)
  396. mBoundingMetrics.leftBearing = 0;
  397. }
  398. if ((NS_MATHML_IS_RTL(mPresentationData.flags) ?
  399. mLeadingSpaceSign : mWidthSign) != NS_MATHML_SIGN_INVALID) {
  400. // there was padding on the right. dismiss the right italic correction now
  401. // (so that our parent won't correct us)
  402. mBoundingMetrics.width = width;
  403. mBoundingMetrics.rightBearing = mBoundingMetrics.width;
  404. }
  405. nscoord dy = height - mBoundingMetrics.ascent;
  406. nscoord dx = NS_MATHML_IS_RTL(mPresentationData.flags) ?
  407. width - initialWidth - lspace : lspace;
  408. aDesiredSize.ascent += dy;
  409. aDesiredSize.width = mBoundingMetrics.width;
  410. aDesiredSize.height += dy + depth - mBoundingMetrics.descent;
  411. mBoundingMetrics.ascent = height;
  412. mBoundingMetrics.descent = depth;
  413. aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  414. mReference.x = 0;
  415. mReference.y = aDesiredSize.ascent;
  416. if (aPlaceOrigin) {
  417. // Finish reflowing child frames, positioning their origins.
  418. PositionRowChildFrames(dx, aDesiredSize.ascent - voffset);
  419. }
  420. return NS_OK;
  421. }