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/. */
6 #include "nsMathMLmtableFrame.h"
7 #include "nsPresContext.h"
8 #include "nsStyleContext.h"
9 #include "nsStyleConsts.h"
10 #include "nsNameSpaceManager.h"
11 #include "nsRenderingContext.h"
12 #include "nsCSSRendering.h"
13 #include "nsMathMLElement.h"
16 #include "nsTableFrame.h"
19 #include "RestyleManager.h"
22 #include "nsIScriptError.h"
23 #include "nsContentUtils.h"
25 using namespace mozilla
;
28 // <mtable> -- table or matrix - implementation
32 ParseStyleValue(nsIAtom
* aAttribute
, const nsAString
& aAttributeValue
)
34 if (aAttribute
== nsGkAtoms::rowalign_
) {
35 if (aAttributeValue
.EqualsLiteral("top"))
36 return NS_STYLE_VERTICAL_ALIGN_TOP
;
37 else if (aAttributeValue
.EqualsLiteral("bottom"))
38 return NS_STYLE_VERTICAL_ALIGN_BOTTOM
;
39 else if (aAttributeValue
.EqualsLiteral("center"))
40 return NS_STYLE_VERTICAL_ALIGN_MIDDLE
;
42 return NS_STYLE_VERTICAL_ALIGN_BASELINE
;
43 } else if (aAttribute
== nsGkAtoms::columnalign_
) {
44 if (aAttributeValue
.EqualsLiteral("left"))
45 return NS_STYLE_TEXT_ALIGN_LEFT
;
46 else if (aAttributeValue
.EqualsLiteral("right"))
47 return NS_STYLE_TEXT_ALIGN_RIGHT
;
49 return NS_STYLE_TEXT_ALIGN_CENTER
;
50 } else if (aAttribute
== nsGkAtoms::rowlines_
||
51 aAttribute
== nsGkAtoms::columnlines_
) {
52 if (aAttributeValue
.EqualsLiteral("solid"))
53 return NS_STYLE_BORDER_STYLE_SOLID
;
54 else if (aAttributeValue
.EqualsLiteral("dashed"))
55 return NS_STYLE_BORDER_STYLE_DASHED
;
57 return NS_STYLE_BORDER_STYLE_NONE
;
59 MOZ_CRASH("Unrecognized attribute.");
65 static nsTArray
<int8_t>*
66 ExtractStyleValues(const nsAString
& aString
, nsIAtom
* aAttribute
,
67 bool aAllowMultiValues
)
69 nsTArray
<int8_t>* styleArray
= nullptr;
71 const char16_t
* start
= aString
.BeginReading();
72 const char16_t
* end
= aString
.EndReading();
74 int32_t startIndex
= 0;
78 // Skip leading spaces.
79 while ((start
< end
) && nsCRT::IsAsciiSpace(*start
)) {
84 // Look for the end of the string, or another space.
85 while ((start
< end
) && !nsCRT::IsAsciiSpace(*start
)) {
90 // Grab the value found and process it.
93 styleArray
= new nsTArray
<int8_t>();
95 // We want to return a null array if an attribute gives multiple values,
96 // but multiple values aren't allowed.
97 if (styleArray
->Length() > 1 && !aAllowMultiValues
) {
102 nsDependentSubstring
valueString(aString
, startIndex
, count
);
103 int8_t styleValue
= ParseStyleValue(aAttribute
, valueString
);
104 styleArray
->AppendElement(styleValue
);
113 static nsresult
ReportParseError(nsIFrame
* aFrame
, const char16_t
* aAttribute
,
114 const char16_t
* aValue
)
116 nsIContent
* content
= aFrame
->GetContent();
118 const char16_t
* params
[] =
119 { aValue
, aAttribute
, content
->Tag()->GetUTF16String() };
121 return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag
,
122 NS_LITERAL_CSTRING("Layout: MathML"),
124 nsContentUtils::eMATHML_PROPERTIES
,
125 "AttributeParsingError", params
, 3);
128 // Each rowalign='top bottom' or columnalign='left right center' (from
129 // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
130 // stored in the property table. Row/Cell frames query the property table
131 // to see what values apply to them.
134 DestroyStylePropertyList(void* aPropertyValue
)
136 delete static_cast<nsTArray
<int8_t>*>(aPropertyValue
);
139 NS_DECLARE_FRAME_PROPERTY(RowAlignProperty
, DestroyStylePropertyList
)
140 NS_DECLARE_FRAME_PROPERTY(RowLinesProperty
, DestroyStylePropertyList
)
141 NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty
, DestroyStylePropertyList
)
142 NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty
, DestroyStylePropertyList
)
144 static const FramePropertyDescriptor
*
145 AttributeToProperty(nsIAtom
* aAttribute
)
147 if (aAttribute
== nsGkAtoms::rowalign_
)
148 return RowAlignProperty();
149 if (aAttribute
== nsGkAtoms::rowlines_
)
150 return RowLinesProperty();
151 if (aAttribute
== nsGkAtoms::columnalign_
)
152 return ColumnAlignProperty();
153 NS_ASSERTION(aAttribute
== nsGkAtoms::columnlines_
, "Invalid attribute");
154 return ColumnLinesProperty();
157 /* This method looks for a property that applies to a cell, but it looks
158 * recursively because some cell properties can come from the cell, a row,
159 * a table, etc. This function searches through the heirarchy for a property
160 * and returns its value. The function stops searching after checking a <mtable>
163 static nsTArray
<int8_t>*
164 FindCellProperty(const nsIFrame
* aCellFrame
,
165 const FramePropertyDescriptor
* aFrameProperty
)
167 const nsIFrame
* currentFrame
= aCellFrame
;
168 nsTArray
<int8_t>* propertyData
= nullptr;
170 while (currentFrame
) {
171 FrameProperties props
= currentFrame
->Properties();
172 propertyData
= static_cast<nsTArray
<int8_t>*>(props
.Get(aFrameProperty
));
173 bool frameIsTable
= (currentFrame
->GetType() == nsGkAtoms::tableFrame
);
175 if (propertyData
|| frameIsTable
)
176 currentFrame
= nullptr; // A null frame pointer exits the loop
178 currentFrame
= currentFrame
->GetParent(); // Go to the parent frame
185 ApplyBorderToStyle(const nsMathMLmtdFrame
* aFrame
,
186 nsStyleBorder
& aStyleBorder
)
190 aFrame
->GetRowIndex(rowIndex
);
191 aFrame
->GetColIndex(columnIndex
);
193 nscoord borderWidth
=
194 aFrame
->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN
];
196 nsTArray
<int8_t>* rowLinesList
=
197 FindCellProperty(aFrame
, RowLinesProperty());
199 nsTArray
<int8_t>* columnLinesList
=
200 FindCellProperty(aFrame
, ColumnLinesProperty());
202 // We don't place a row line on top of the first row
203 if (rowIndex
> 0 && rowLinesList
) {
204 // If the row number is greater than the number of provided rowline
205 // values, we simply repeat the last value.
206 int32_t listLength
= rowLinesList
->Length();
207 if (rowIndex
< listLength
) {
208 aStyleBorder
.SetBorderStyle(NS_SIDE_TOP
,
209 rowLinesList
->ElementAt(rowIndex
- 1));
211 aStyleBorder
.SetBorderStyle(NS_SIDE_TOP
,
212 rowLinesList
->ElementAt(listLength
- 1));
214 aStyleBorder
.SetBorderWidth(NS_SIDE_TOP
, borderWidth
);
217 // We don't place a column line on the left of the first column.
218 if (columnIndex
> 0 && columnLinesList
) {
219 // If the column number is greater than the number of provided columline
220 // values, we simply repeat the last value.
221 int32_t listLength
= columnLinesList
->Length();
222 if (columnIndex
< listLength
) {
223 aStyleBorder
.SetBorderStyle(NS_SIDE_LEFT
,
224 columnLinesList
->ElementAt(columnIndex
- 1));
226 aStyleBorder
.SetBorderStyle(NS_SIDE_LEFT
,
227 columnLinesList
->ElementAt(listLength
- 1));
229 aStyleBorder
.SetBorderWidth(NS_SIDE_LEFT
, borderWidth
);
234 ComputeBorderOverflow(nsMathMLmtdFrame
* aFrame
, nsStyleBorder aStyleBorder
)
239 nsMathMLmtableFrame
* mathMLmtableFrame
=
240 static_cast<nsMathMLmtableFrame
*>(nsTableFrame::GetTableFrame(aFrame
));
241 aFrame
->GetCellIndexes(rowIndex
, columnIndex
);
243 overflow
.left
= mathMLmtableFrame
->GetCellSpacingX(-1);
244 overflow
.right
= mathMLmtableFrame
->GetCellSpacingX(0) / 2;
245 } else if (columnIndex
== mathMLmtableFrame
->GetColCount() - 1) {
246 overflow
.left
= mathMLmtableFrame
->GetCellSpacingX(columnIndex
- 1) / 2;
247 overflow
.right
= mathMLmtableFrame
->GetCellSpacingX(columnIndex
+ 1);
249 overflow
.left
= mathMLmtableFrame
->GetCellSpacingX(columnIndex
- 1) / 2;
250 overflow
.right
= mathMLmtableFrame
->GetCellSpacingX(columnIndex
) / 2;
253 overflow
.top
= mathMLmtableFrame
->GetCellSpacingY(-1);
254 overflow
.bottom
= mathMLmtableFrame
->GetCellSpacingY(0) / 2;
255 } else if (rowIndex
== mathMLmtableFrame
->GetRowCount() - 1) {
256 overflow
.top
= mathMLmtableFrame
->GetCellSpacingY(rowIndex
- 1) / 2;
257 overflow
.bottom
= mathMLmtableFrame
->GetCellSpacingY(rowIndex
+ 1);
259 overflow
.top
= mathMLmtableFrame
->GetCellSpacingY(rowIndex
- 1) / 2;
260 overflow
.bottom
= mathMLmtableFrame
->GetCellSpacingY(rowIndex
) / 2;
266 * A variant of the nsDisplayBorder contains special code to render a border
267 * around a nsMathMLmtdFrame based on the rowline and columnline properties
268 * set on the cell frame.
270 class nsDisplaymtdBorder
: public nsDisplayBorder
{
272 nsDisplaymtdBorder(nsDisplayListBuilder
* aBuilder
, nsMathMLmtdFrame
* aFrame
)
273 : nsDisplayBorder(aBuilder
, aFrame
)
277 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
, bool* aSnap
) MOZ_OVERRIDE
279 nsStyleBorder styleBorder
= *mFrame
->StyleBorder();
280 nsMathMLmtdFrame
* frame
= static_cast<nsMathMLmtdFrame
*>(mFrame
);
281 ApplyBorderToStyle(frame
, styleBorder
);
282 nsRect bounds
= CalculateBounds(styleBorder
);
283 nsMargin overflow
= ComputeBorderOverflow(frame
, styleBorder
);
284 bounds
.Inflate(overflow
);
288 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsRenderingContext
* aCtx
) MOZ_OVERRIDE
290 nsStyleBorder styleBorder
= *mFrame
->StyleBorder();
291 nsMathMLmtdFrame
* frame
= static_cast<nsMathMLmtdFrame
*>(mFrame
);
292 ApplyBorderToStyle(frame
, styleBorder
);
294 nsRect bounds
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
295 nsMargin overflow
= ComputeBorderOverflow(frame
, styleBorder
);
296 bounds
.Inflate(overflow
);
298 nsCSSRendering::PaintBorderWithStyleBorder(mFrame
->PresContext(), *aCtx
,
299 mFrame
, mVisibleRect
,
302 mFrame
->StyleContext(),
303 mFrame
->GetSkipSides());
308 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
309 NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->StyleDisplay()->mDisplay, "internal error");
311 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
315 ParseFrameAttribute(nsIFrame
* aFrame
, nsIAtom
* aAttribute
,
316 bool aAllowMultiValues
)
318 nsAutoString attrValue
;
320 nsIContent
* frameContent
= aFrame
->GetContent();
321 frameContent
->GetAttr(kNameSpaceID_None
, aAttribute
, attrValue
);
323 if (!attrValue
.IsEmpty()) {
324 nsTArray
<int8_t>* valueList
=
325 ExtractStyleValues(attrValue
, aAttribute
, aAllowMultiValues
);
327 // If valueList is null, that indicates a problem with the attribute value.
328 // Only set properties on a valid attribute value.
330 // The code reading the property assumes that this list is nonempty.
331 NS_ASSERTION(valueList
->Length() >= 1, "valueList should not be empty!");
332 FrameProperties props
= aFrame
->Properties();
333 props
.Set(AttributeToProperty(aAttribute
), valueList
);
335 ReportParseError(aFrame
, aAttribute
->GetUTF16String(), attrValue
.get());
342 // Specifies the distance between successive rows in an mtable. Multiple
343 // lengths can be specified, each corresponding to its respective position
344 // between rows. For example:
352 // If the number of row gaps exceeds the number of lengths specified, the final
353 // specified length is repeated. Additional lengths are ignored.
358 // Unitless values are permitted and provide a multiple of the default value
359 // Negative values are forbidden.
364 // Specifies the distance between successive columns in an mtable. Multiple
365 // lengths can be specified, each corresponding to its respective position
366 // between columns. For example:
368 // [COLUMN_0] columnspace_0 [COLUMN_1] columnspace_1 [COLUMN_2]
370 // If the number of column gaps exceeds the number of lengths specified, the
371 // final specified length is repeated. Additional lengths are ignored.
376 // Unitless values are permitted and provide a multiple of the default value
377 // Negative values are forbidden.
382 // Specifies the distance between the mtable and its frame (if any). The
383 // first value specified provides the spacing between the left and right edge
384 // of the table and the frame, the second value determines the spacing between
385 // the top and bottom edges and the frame.
387 // An error is reported if only one length is passed. Any additional lengths
390 // values: length length
391 // default: 0em 0ex If frame attribute is "none" or not specified,
392 // 0.4em 0.5ex otherwise
394 // Unitless values are permitted and provide a multiple of the default value
395 // Negative values are forbidden.
398 static const float kDefaultRowspacingEx
= 1.0f
;
399 static const float kDefaultColumnspacingEm
= 0.8f
;
400 static const float kDefaultFramespacingArg0Em
= 0.4f
;
401 static const float kDefaultFramespacingArg1Ex
= 0.5f
;
404 ExtractSpacingValues(const nsAString
& aString
,
406 nsTArray
<nscoord
>& aSpacingArray
,
408 nscoord aDefaultValue0
,
409 nscoord aDefaultValue1
)
411 nsPresContext
* presContext
= aFrame
->PresContext();
412 nsStyleContext
* styleContext
= aFrame
->StyleContext();
414 const char16_t
* start
= aString
.BeginReading();
415 const char16_t
* end
= aString
.EndReading();
417 int32_t startIndex
= 0;
419 int32_t elementNum
= 0;
421 while (start
< end
) {
422 // Skip leading spaces.
423 while ((start
< end
) && nsCRT::IsAsciiSpace(*start
)) {
428 // Look for the end of the string, or another space.
429 while ((start
< end
) && !nsCRT::IsAsciiSpace(*start
)) {
434 // Grab the value found and process it.
436 const nsAString
& str
= Substring(aString
, startIndex
, count
);
437 nsAutoString valueString
;
438 valueString
.Assign(str
);
440 if (aAttribute
== nsGkAtoms::framespacing_
&& elementNum
) {
441 newValue
= aDefaultValue1
;
443 newValue
= aDefaultValue0
;
445 nsMathMLFrame::ParseNumericValue(valueString
, &newValue
,
446 nsMathMLElement::PARSE_ALLOW_UNITLESS
,
447 presContext
, styleContext
);
448 aSpacingArray
.AppendElement(newValue
);
458 ParseSpacingAttribute(nsMathMLmtableFrame
* aFrame
, nsIAtom
* aAttribute
)
460 NS_ASSERTION(aAttribute
== nsGkAtoms::rowspacing_
||
461 aAttribute
== nsGkAtoms::columnspacing_
||
462 aAttribute
== nsGkAtoms::framespacing_
,
463 "Non spacing attribute passed");
465 nsAutoString attrValue
;
466 nsIContent
* frameContent
= aFrame
->GetContent();
467 frameContent
->GetAttr(kNameSpaceID_None
, aAttribute
, attrValue
);
469 if (nsGkAtoms::framespacing_
== aAttribute
) {
471 frameContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::frame
, frame
);
472 if (frame
.IsEmpty() || frame
.EqualsLiteral("none")) {
473 aFrame
->SetFrameSpacing(0, 0);
481 nsRefPtr
<nsFontMetrics
> fm
;
482 nsLayoutUtils::GetFontMetricsForFrame(aFrame
, getter_AddRefs(fm
));
483 if (nsGkAtoms::rowspacing_
== aAttribute
) {
484 value
= kDefaultRowspacingEx
* fm
->XHeight();
486 } else if (nsGkAtoms::columnspacing_
== aAttribute
) {
487 value
= kDefaultColumnspacingEm
* fm
->EmHeight();
490 value
= kDefaultFramespacingArg0Em
* fm
->EmHeight();
491 value2
= kDefaultFramespacingArg1Ex
* fm
->XHeight();
494 nsTArray
<nscoord
> valueList
;
495 ExtractSpacingValues(attrValue
, aAttribute
, valueList
, aFrame
, value
, value2
);
496 if (valueList
.Length() == 0) {
497 if (frameContent
->HasAttr(kNameSpaceID_None
, aAttribute
)) {
498 ReportParseError(aFrame
, aAttribute
->GetUTF16String(),
501 valueList
.AppendElement(value
);
503 if (aAttribute
== nsGkAtoms::framespacing_
) {
504 if (valueList
.Length() == 1) {
505 if(frameContent
->HasAttr(kNameSpaceID_None
, aAttribute
)) {
506 ReportParseError(aFrame
, aAttribute
->GetUTF16String(),
509 valueList
.AppendElement(value2
);
510 } else if (valueList
.Length() != 2) {
511 ReportParseError(aFrame
, aAttribute
->GetUTF16String(),
516 if (aAttribute
== nsGkAtoms::rowspacing_
) {
517 aFrame
->SetRowSpacingArray(valueList
);
518 } else if (aAttribute
== nsGkAtoms::columnspacing_
) {
519 aFrame
->SetColSpacingArray(valueList
);
521 aFrame
->SetFrameSpacing(valueList
.ElementAt(0),
522 valueList
.ElementAt(1));
526 static void ParseSpacingAttributes(nsMathMLmtableFrame
* aTableFrame
)
528 ParseSpacingAttribute(aTableFrame
, nsGkAtoms::rowspacing_
);
529 ParseSpacingAttribute(aTableFrame
, nsGkAtoms::columnspacing_
);
530 ParseSpacingAttribute(aTableFrame
, nsGkAtoms::framespacing_
);
531 aTableFrame
->SetUseCSSSpacing();
534 // map all attribues within a table -- requires the indices of rows and cells.
535 // so it can only happen after they are made ready by the table base class.
537 MapAllAttributesIntoCSS(nsMathMLmtableFrame
* aTableFrame
)
539 // Map mtable rowalign & rowlines.
540 ParseFrameAttribute(aTableFrame
, nsGkAtoms::rowalign_
, true);
541 ParseFrameAttribute(aTableFrame
, nsGkAtoms::rowlines_
, true);
543 // Map mtable columnalign & columnlines.
544 ParseFrameAttribute(aTableFrame
, nsGkAtoms::columnalign_
, true);
545 ParseFrameAttribute(aTableFrame
, nsGkAtoms::columnlines_
, true);
547 // Map mtable rowspacing, columnspacing & framespacing
548 ParseSpacingAttributes(aTableFrame
);
550 // mtable is simple and only has one (pseudo) row-group
551 nsIFrame
* rgFrame
= aTableFrame
->GetFirstPrincipalChild();
552 if (!rgFrame
|| rgFrame
->GetType() != nsGkAtoms::tableRowGroupFrame
)
555 nsIFrame
* rowFrame
= rgFrame
->GetFirstPrincipalChild();
556 for ( ; rowFrame
; rowFrame
= rowFrame
->GetNextSibling()) {
557 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame
, TABLE_ROW
);
558 if (rowFrame
->GetType() == nsGkAtoms::tableRowFrame
) {
560 ParseFrameAttribute(rowFrame
, nsGkAtoms::rowalign_
, false);
561 // Map row columnalign.
562 ParseFrameAttribute(rowFrame
, nsGkAtoms::columnalign_
, true);
564 nsIFrame
* cellFrame
= rowFrame
->GetFirstPrincipalChild();
565 for ( ; cellFrame
; cellFrame
= cellFrame
->GetNextSibling()) {
566 DEBUG_VERIFY_THAT_FRAME_IS(cellFrame
, TABLE_CELL
);
567 if (IS_TABLE_CELL(cellFrame
->GetType())) {
568 // Map cell rowalign.
569 ParseFrameAttribute(cellFrame
, nsGkAtoms::rowalign_
, false);
570 // Map row columnalign.
571 ParseFrameAttribute(cellFrame
, nsGkAtoms::columnalign_
, false);
578 // the align attribute of mtable can have a row number which indicates
579 // from where to anchor the table, e.g., top 5 means anchor the table at
580 // the top of the 5th row, axis -1 means anchor the table on the axis of
583 // The REC says that the syntax is
584 // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*'
585 // the parsing could have been simpler with that syntax
586 // but for backward compatibility we make optional
587 // the whitespaces between the alignment name and the row number
598 ParseAlignAttribute(nsString
& aValue
, eAlign
& aAlign
, int32_t& aRowIndex
)
600 // by default, the table is centered about the axis
602 aAlign
= eAlign_axis
;
605 // we only have to remove the leading spaces because
606 // ToInteger ignores the whitespaces around the number
607 aValue
.CompressWhitespace(true, false);
609 if (0 == aValue
.Find("top")) {
610 len
= 3; // 3 is the length of 'top'
613 else if (0 == aValue
.Find("bottom")) {
614 len
= 6; // 6 is the length of 'bottom'
615 aAlign
= eAlign_bottom
;
617 else if (0 == aValue
.Find("center")) {
618 len
= 6; // 6 is the length of 'center'
619 aAlign
= eAlign_center
;
621 else if (0 == aValue
.Find("baseline")) {
622 len
= 8; // 8 is the length of 'baseline'
623 aAlign
= eAlign_baseline
;
625 else if (0 == aValue
.Find("axis")) {
626 len
= 4; // 4 is the length of 'axis'
627 aAlign
= eAlign_axis
;
631 aValue
.Cut(0, len
); // aValue is not a const here
632 aRowIndex
= aValue
.ToInteger(&error
);
633 if (NS_FAILED(error
))
639 // call ListMathMLTree(mParent) to get the big picture
641 ListMathMLTree(nsIFrame
* atLeast
)
643 // climb up to <math> or <body> if <math> isn't there
644 nsIFrame
* f
= atLeast
;
645 for ( ; f
; f
= f
->GetParent()) {
646 nsIContent
* c
= f
->GetContent();
647 if (!c
|| c
->Tag() == nsGkAtoms::math
|| c
->Tag() == nsGkAtoms::body
)
656 // implementation of nsMathMLmtableOuterFrame
658 NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame
)
659 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
660 NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame
)
663 NS_NewMathMLmtableOuterFrame (nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
665 return new (aPresShell
) nsMathMLmtableOuterFrame(aContext
);
668 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame
)
670 nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
675 nsMathMLmtableOuterFrame::AttributeChanged(int32_t aNameSpaceID
,
679 // Attributes specific to <mtable>:
680 // frame : in mathml.css
681 // framespacing : here
682 // groupalign : not yet supported
683 // equalrows : not yet supported
684 // equalcolumns : not yet supported
685 // displaystyle : here and in mathml.css
690 // columnalign : here
691 // columnlines : here
692 // columnspacing : here
694 // mtable is simple and only has one (pseudo) row-group inside our inner-table
695 nsIFrame
* tableFrame
= mFrames
.FirstChild();
696 NS_ASSERTION(tableFrame
&& tableFrame
->GetType() == nsGkAtoms::tableFrame
,
697 "should always have an inner table frame");
698 nsIFrame
* rgFrame
= tableFrame
->GetFirstPrincipalChild();
699 if (!rgFrame
|| rgFrame
->GetType() != nsGkAtoms::tableRowGroupFrame
)
702 // align - just need to issue a dirty (resize) reflow command
703 if (aAttribute
== nsGkAtoms::align
) {
704 PresContext()->PresShell()->
705 FrameNeedsReflow(this, nsIPresShell::eResize
, NS_FRAME_IS_DIRTY
);
709 // displaystyle - may seem innocuous, but it is actually very harsh --
710 // like changing an unit. Blow away and recompute all our automatic
711 // presentational data, and issue a style-changed reflow request
712 if (aAttribute
== nsGkAtoms::displaystyle_
) {
713 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(GetParent());
714 // Need to reflow the parent, not us, because this can actually
716 PresContext()->PresShell()->
717 FrameNeedsReflow(GetParent(), nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
721 // ...and the other attributes affect rows or columns in one way or another
723 nsPresContext
* presContext
= tableFrame
->PresContext();
724 if (aAttribute
== nsGkAtoms::rowspacing_
||
725 aAttribute
== nsGkAtoms::columnspacing_
||
726 aAttribute
== nsGkAtoms::framespacing_
) {
727 nsMathMLmtableFrame
* mathMLmtableFrame
= do_QueryFrame(tableFrame
);
728 if (mathMLmtableFrame
) {
729 ParseSpacingAttribute(mathMLmtableFrame
, aAttribute
);
730 mathMLmtableFrame
->SetUseCSSSpacing();
732 } else if (aAttribute
== nsGkAtoms::rowalign_
||
733 aAttribute
== nsGkAtoms::rowlines_
||
734 aAttribute
== nsGkAtoms::columnalign_
||
735 aAttribute
== nsGkAtoms::columnlines_
) {
736 // clear any cached property list for this table
737 presContext
->PropertyTable()->
738 Delete(tableFrame
, AttributeToProperty(aAttribute
));
739 // Reparse the new attribute on the table.
740 ParseFrameAttribute(tableFrame
, aAttribute
, true);
742 // Ignore attributes that do not affect layout.
746 // Explicitly request a reflow in our subtree to pick up any changes
747 presContext
->PresShell()->
748 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
754 nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext
* aPresContext
,
757 int32_t rowCount
= GetRowCount();
759 // Negative indices mean to find upwards from the end.
761 aRowIndex
= rowCount
+ aRowIndex
;
763 // aRowIndex is 1-based, so convert it to a 0-based index
767 // if our inner table says that the index is valid, find the row now
768 if (0 <= aRowIndex
&& aRowIndex
<= rowCount
) {
769 nsIFrame
* tableFrame
= mFrames
.FirstChild();
770 NS_ASSERTION(tableFrame
&& tableFrame
->GetType() == nsGkAtoms::tableFrame
,
771 "should always have an inner table frame");
772 nsIFrame
* rgFrame
= tableFrame
->GetFirstPrincipalChild();
773 if (!rgFrame
|| rgFrame
->GetType() != nsGkAtoms::tableRowGroupFrame
)
775 nsTableIterator
rowIter(*rgFrame
);
776 nsIFrame
* rowFrame
= rowIter
.First();
777 for ( ; rowFrame
; rowFrame
= rowIter
.Next()) {
778 if (aRowIndex
== 0) {
779 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame
, TABLE_ROW
);
780 if (rowFrame
->GetType() != nsGkAtoms::tableRowFrame
)
792 nsMathMLmtableOuterFrame::Reflow(nsPresContext
* aPresContext
,
793 nsHTMLReflowMetrics
& aDesiredSize
,
794 const nsHTMLReflowState
& aReflowState
,
795 nsReflowStatus
& aStatus
)
798 // we want to return a table that is anchored according to the align attribute
800 nsTableOuterFrame::Reflow(aPresContext
, aDesiredSize
, aReflowState
, aStatus
);
801 NS_ASSERTION(aDesiredSize
.Height() >= 0, "illegal height for mtable");
802 NS_ASSERTION(aDesiredSize
.Width() >= 0, "illegal width for mtable");
804 // see if the user has set the align attribute on the <mtable>
805 int32_t rowIndex
= 0;
806 eAlign tableAlign
= eAlign_axis
;
807 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::align
, value
);
808 if (!value
.IsEmpty()) {
809 ParseAlignAttribute(value
, tableAlign
, rowIndex
);
812 // adjustments if there is a specified row from where to anchor the table
813 // (conceptually: when there is no row of reference, picture the table as if
814 // it is wrapped in a single big fictional row at dy = 0, this way of
815 // doing so allows us to have a single code path for all cases).
817 WritingMode wm
= aDesiredSize
.GetWritingMode();
818 nscoord blockSize
= aDesiredSize
.BSize(wm
);
819 nsIFrame
* rowFrame
= nullptr;
821 rowFrame
= GetRowFrameAt(aPresContext
, rowIndex
);
823 // translate the coordinates to be relative to us and in our writing mode
824 nsIFrame
* frame
= rowFrame
;
825 LogicalRect
frameRect(wm
, frame
->GetRect(), aReflowState
.ComputedWidth());
826 blockSize
= frameRect
.BSize(wm
);
828 dy
+= frameRect
.BStart(wm
);
829 frame
= frame
->GetParent();
830 } while (frame
!= this);
833 switch (tableAlign
) {
835 aDesiredSize
.SetBlockStartAscent(dy
);
838 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
);
841 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
/ 2);
843 case eAlign_baseline
:
845 // anchor the table on the baseline of the row of reference
846 nscoord rowAscent
= ((nsTableRowFrame
*)rowFrame
)->GetMaxCellAscent();
847 if (rowAscent
) { // the row has at least one cell with 'vertical-align: baseline'
848 aDesiredSize
.SetBlockStartAscent(dy
+ rowAscent
);
852 // in other situations, fallback to center
853 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
/ 2);
857 // XXX should instead use style data from the row of reference here ?
858 nsRefPtr
<nsFontMetrics
> fm
;
859 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
860 aReflowState
.rendContext
->SetFont(fm
);
862 GetAxisHeight(*aReflowState
.rendContext
,
863 aReflowState
.rendContext
->FontMetrics(),
866 // anchor the table on the axis of the row of reference
867 // XXX fallback to baseline because it is a hard problem
868 // XXX need to fetch the axis of the row; would need rowalign=axis to work better
869 nscoord rowAscent
= ((nsTableRowFrame
*)rowFrame
)->GetMaxCellAscent();
870 if (rowAscent
) { // the row has at least one cell with 'vertical-align: baseline'
871 aDesiredSize
.SetBlockStartAscent(dy
+ rowAscent
);
875 // in other situations, fallback to using half of the height
876 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
/ 2 + axisHeight
);
881 mReference
.y
= aDesiredSize
.BlockStartAscent();
883 // just make-up a bounding metrics
884 mBoundingMetrics
= nsBoundingMetrics();
885 mBoundingMetrics
.ascent
= aDesiredSize
.BlockStartAscent();
886 mBoundingMetrics
.descent
= aDesiredSize
.Height() -
887 aDesiredSize
.BlockStartAscent();
888 mBoundingMetrics
.width
= aDesiredSize
.Width();
889 mBoundingMetrics
.leftBearing
= 0;
890 mBoundingMetrics
.rightBearing
= aDesiredSize
.Width();
892 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
893 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
897 NS_NewMathMLmtableFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
899 return new (aPresShell
) nsMathMLmtableFrame(aContext
);
902 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame
)
904 nsMathMLmtableFrame::~nsMathMLmtableFrame()
909 nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID
,
910 nsFrameList
& aChildList
)
912 nsTableFrame::SetInitialChildList(aListID
, aChildList
);
913 MapAllAttributesIntoCSS(this);
917 nsMathMLmtableFrame::RestyleTable()
919 // re-sync MathML specific style data that may have changed
920 MapAllAttributesIntoCSS(this);
922 // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
923 PresContext()->RestyleManager()->
924 PostRestyleEvent(mContent
->AsElement(), eRestyle_Subtree
,
925 nsChangeHint_AllReflowHints
);
929 nsMathMLmtableFrame::GetCellSpacingX(int32_t aColIndex
)
931 if (mUseCSSSpacing
) {
932 return nsTableFrame::GetCellSpacingX(aColIndex
);
934 if (!mColSpacing
.Length()) {
935 NS_ERROR("mColSpacing should not be empty");
938 if (aColIndex
< 0 || aColIndex
>= GetColCount()) {
939 NS_ASSERTION(aColIndex
== -1 || aColIndex
== GetColCount(),
940 "Desired column beyond bounds of table and border");
941 return mFrameSpacingX
;
943 if ((uint32_t) aColIndex
>= mColSpacing
.Length()) {
944 return mColSpacing
.LastElement();
946 return mColSpacing
.ElementAt(aColIndex
);
950 nsMathMLmtableFrame::GetCellSpacingX(int32_t aStartColIndex
,
951 int32_t aEndColIndex
)
953 if (mUseCSSSpacing
) {
954 return nsTableFrame::GetCellSpacingX(aStartColIndex
, aEndColIndex
);
956 if (aStartColIndex
== aEndColIndex
) {
959 if (!mColSpacing
.Length()) {
960 NS_ERROR("mColSpacing should not be empty");
964 if (aStartColIndex
< 0) {
965 NS_ASSERTION(aStartColIndex
== -1,
966 "Desired column beyond bounds of table and border");
967 space
+= mFrameSpacingX
;
970 if (aEndColIndex
>= GetColCount()) {
971 NS_ASSERTION(aEndColIndex
== GetColCount(),
972 "Desired column beyond bounds of table and border");
973 space
+= mFrameSpacingX
;
974 aEndColIndex
= GetColCount();
976 // Only iterate over column spacing when there is the potential to vary
977 int32_t min
= std::min(aEndColIndex
, (int32_t) mColSpacing
.Length());
978 for (int32_t i
= aStartColIndex
; i
< min
; i
++) {
979 space
+= mColSpacing
.ElementAt(i
);
981 // The remaining values are constant. Note that if there are more
982 // column spacings specified than there are columns, LastElement() will be
983 // multiplied by 0, so it is still safe to use.
984 space
+= (aEndColIndex
- min
) * mColSpacing
.LastElement();
989 nsMathMLmtableFrame::GetCellSpacingY(int32_t aRowIndex
)
991 if (mUseCSSSpacing
) {
992 return nsTableFrame::GetCellSpacingY(aRowIndex
);
994 if (!mRowSpacing
.Length()) {
995 NS_ERROR("mRowSpacing should not be empty");
998 if (aRowIndex
< 0 || aRowIndex
>= GetRowCount()) {
999 NS_ASSERTION(aRowIndex
== -1 || aRowIndex
== GetRowCount(),
1000 "Desired row beyond bounds of table and border");
1001 return mFrameSpacingY
;
1003 if ((uint32_t) aRowIndex
>= mRowSpacing
.Length()) {
1004 return mRowSpacing
.LastElement();
1006 return mRowSpacing
.ElementAt(aRowIndex
);
1010 nsMathMLmtableFrame::GetCellSpacingY(int32_t aStartRowIndex
,
1011 int32_t aEndRowIndex
)
1013 if (mUseCSSSpacing
) {
1014 return nsTableFrame::GetCellSpacingY(aStartRowIndex
, aEndRowIndex
);
1016 if (aStartRowIndex
== aEndRowIndex
) {
1019 if (!mRowSpacing
.Length()) {
1020 NS_ERROR("mRowSpacing should not be empty");
1024 if (aStartRowIndex
< 0) {
1025 NS_ASSERTION(aStartRowIndex
== -1,
1026 "Desired row beyond bounds of table and border");
1027 space
+= mFrameSpacingY
;
1030 if (aEndRowIndex
>= GetRowCount()) {
1031 NS_ASSERTION(aEndRowIndex
== GetRowCount(),
1032 "Desired row beyond bounds of table and border");
1033 space
+= mFrameSpacingY
;
1034 aEndRowIndex
= GetRowCount();
1036 // Only iterate over row spacing when there is the potential to vary
1037 int32_t min
= std::min(aEndRowIndex
, (int32_t) mRowSpacing
.Length());
1038 for (int32_t i
= aStartRowIndex
; i
< min
; i
++) {
1039 space
+= mRowSpacing
.ElementAt(i
);
1041 // The remaining values are constant. Note that if there are more
1042 // row spacings specified than there are row, LastElement() will be
1043 // multiplied by 0, so it is still safe to use.
1044 space
+= (aEndRowIndex
- min
) * mRowSpacing
.LastElement();
1049 nsMathMLmtableFrame::SetUseCSSSpacing()
1052 !(mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::rowspacing_
) ||
1053 mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::columnspacing_
) ||
1054 mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::framespacing_
));
1057 NS_QUERYFRAME_HEAD(nsMathMLmtableFrame
)
1058 NS_QUERYFRAME_ENTRY(nsMathMLmtableFrame
)
1059 NS_QUERYFRAME_TAIL_INHERITING(nsTableFrame
)
1062 // implementation of nsMathMLmtrFrame
1065 NS_NewMathMLmtrFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1067 return new (aPresShell
) nsMathMLmtrFrame(aContext
);
1070 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame
)
1072 nsMathMLmtrFrame::~nsMathMLmtrFrame()
1077 nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID
,
1078 nsIAtom
* aAttribute
,
1081 // Attributes specific to <mtr>:
1082 // groupalign : Not yet supported.
1084 // columnalign : Here
1086 nsPresContext
* presContext
= PresContext();
1088 if (aAttribute
!= nsGkAtoms::rowalign_
&&
1089 aAttribute
!= nsGkAtoms::columnalign_
) {
1093 presContext
->PropertyTable()->Delete(this, AttributeToProperty(aAttribute
));
1095 bool allowMultiValues
= (aAttribute
== nsGkAtoms::columnalign_
);
1097 // Reparse the new attribute.
1098 ParseFrameAttribute(this, aAttribute
, allowMultiValues
);
1100 // Explicitly request a reflow in our subtree to pick up any changes
1101 presContext
->PresShell()->
1102 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
1108 // implementation of nsMathMLmtdFrame
1111 NS_NewMathMLmtdFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1113 return new (aPresShell
) nsMathMLmtdFrame(aContext
);
1116 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame
)
1118 nsMathMLmtdFrame::~nsMathMLmtdFrame()
1123 nsMathMLmtdFrame::GetRowSpan()
1125 int32_t rowspan
= 1;
1127 // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
1128 if ((mContent
->Tag() == nsGkAtoms::mtd_
) && !StyleContext()->GetPseudo()) {
1130 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::rowspan
, value
);
1131 if (!value
.IsEmpty()) {
1133 rowspan
= value
.ToInteger(&error
);
1134 if (NS_FAILED(error
) || rowspan
< 0)
1136 rowspan
= std::min(rowspan
, MAX_ROWSPAN
);
1143 nsMathMLmtdFrame::GetColSpan()
1145 int32_t colspan
= 1;
1147 // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
1148 if ((mContent
->Tag() == nsGkAtoms::mtd_
) && !StyleContext()->GetPseudo()) {
1150 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::columnspan_
, value
);
1151 if (!value
.IsEmpty()) {
1153 colspan
= value
.ToInteger(&error
);
1154 if (NS_FAILED(error
) || colspan
< 0 || colspan
> MAX_COLSPAN
)
1162 nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID
,
1163 nsIAtom
* aAttribute
,
1166 // Attributes specific to <mtd>:
1167 // groupalign : Not yet supported
1169 // columnalign : here
1171 // columnspan : here
1173 if (aAttribute
== nsGkAtoms::rowalign_
||
1174 aAttribute
== nsGkAtoms::columnalign_
) {
1176 nsPresContext
* presContext
= PresContext();
1177 presContext
->PropertyTable()->Delete(this, AttributeToProperty(aAttribute
));
1179 // Reparse the attribute.
1180 ParseFrameAttribute(this, aAttribute
, false);
1184 if (aAttribute
== nsGkAtoms::rowspan
||
1185 aAttribute
== nsGkAtoms::columnspan_
) {
1186 // use the naming expected by the base class
1187 if (aAttribute
== nsGkAtoms::columnspan_
)
1188 aAttribute
= nsGkAtoms::colspan
;
1189 return nsTableCellFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
1196 nsMathMLmtdFrame::GetVerticalAlign() const
1198 // Set the default alignment in case no alignment was specified
1199 uint8_t alignment
= nsTableCellFrame::GetVerticalAlign();
1201 nsTArray
<int8_t>* alignmentList
= FindCellProperty(this, RowAlignProperty());
1203 if (alignmentList
) {
1205 GetRowIndex(rowIndex
);
1207 // If the row number is greater than the number of provided rowalign values,
1208 // we simply repeat the last value.
1209 if (rowIndex
< (int32_t)alignmentList
->Length())
1210 alignment
= alignmentList
->ElementAt(rowIndex
);
1212 alignment
= alignmentList
->ElementAt(alignmentList
->Length() - 1);
1219 nsMathMLmtdFrame::ProcessBorders(nsTableFrame
* aFrame
,
1220 nsDisplayListBuilder
* aBuilder
,
1221 const nsDisplayListSet
& aLists
)
1223 aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
1224 nsDisplaymtdBorder(aBuilder
, this));
1229 nsMathMLmtdFrame::GetBorderWidth(nsMargin
& aBorder
) const
1231 nsStyleBorder styleBorder
= *StyleBorder();
1232 ApplyBorderToStyle(this, styleBorder
);
1233 aBorder
= styleBorder
.GetComputedBorder();
1238 nsMathMLmtdFrame::GetBorderOverflow()
1240 nsStyleBorder styleBorder
= *StyleBorder();
1241 ApplyBorderToStyle(this, styleBorder
);
1242 nsMargin overflow
= ComputeBorderOverflow(this, styleBorder
);
1247 // implementation of nsMathMLmtdInnerFrame
1249 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame
)
1250 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
1251 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
1254 NS_NewMathMLmtdInnerFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1256 return new (aPresShell
) nsMathMLmtdInnerFrame(aContext
);
1259 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame
)
1261 nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext
* aContext
)
1262 : nsBlockFrame(aContext
)
1264 // Make a copy of the parent nsStyleText for later modificaiton.
1265 mUniqueStyleText
= new (PresContext()) nsStyleText(*StyleText());
1268 nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
1270 mUniqueStyleText
->Destroy(PresContext());
1274 nsMathMLmtdInnerFrame::Reflow(nsPresContext
* aPresContext
,
1275 nsHTMLReflowMetrics
& aDesiredSize
,
1276 const nsHTMLReflowState
& aReflowState
,
1277 nsReflowStatus
& aStatus
)
1279 // Let the base class do the reflow
1280 nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, aReflowState
, aStatus
);
1282 // more about <maligngroup/> and <malignmark/> later
1287 nsStyleText
* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
1289 // Set the default alignment in case nothing was specified
1290 uint8_t alignment
= StyleText()->mTextAlign
;
1292 nsTArray
<int8_t>* alignmentList
=
1293 FindCellProperty(this, ColumnAlignProperty());
1295 if (alignmentList
) {
1296 nsMathMLmtdFrame
* cellFrame
= (nsMathMLmtdFrame
*)GetParent();
1297 int32_t columnIndex
;
1298 cellFrame
->GetColIndex(columnIndex
);
1300 // If the column number is greater than the number of provided columalign
1301 // values, we simply repeat the last value.
1302 if (columnIndex
< (int32_t)alignmentList
->Length())
1303 alignment
= alignmentList
->ElementAt(columnIndex
);
1305 alignment
= alignmentList
->ElementAt(alignmentList
->Length() - 1);
1308 mUniqueStyleText
->mTextAlign
= alignment
;
1309 return mUniqueStyleText
;
1313 nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
1315 nsBlockFrame::DidSetStyleContext(aOldStyleContext
);
1316 mUniqueStyleText
->Destroy(PresContext());
1317 mUniqueStyleText
= new (PresContext()) nsStyleText(*StyleText());