1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "gfxContext.h"
8 #include "nsMathMLmtableFrame.h"
9 #include "nsPresContext.h"
10 #include "nsStyleConsts.h"
11 #include "nsNameSpaceManager.h"
12 #include "nsCSSRendering.h"
13 #include "mozilla/dom/MathMLElement.h"
17 #include "nsTableFrame.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/RestyleManager.h"
24 #include "nsIScriptError.h"
25 #include "nsContentUtils.h"
26 #include "nsLayoutUtils.h"
28 using namespace mozilla
;
29 using namespace mozilla::image
;
30 using mozilla::dom::Element
;
33 // <mtable> -- table or matrix - implementation
36 static int8_t ParseStyleValue(nsAtom
* aAttribute
,
37 const nsAString
& aAttributeValue
) {
38 if (aAttribute
== nsGkAtoms::rowalign_
) {
39 if (aAttributeValue
.EqualsLiteral("top")) {
40 return static_cast<int8_t>(StyleVerticalAlignKeyword::Top
);
42 if (aAttributeValue
.EqualsLiteral("bottom")) {
43 return static_cast<int8_t>(StyleVerticalAlignKeyword::Bottom
);
45 if (aAttributeValue
.EqualsLiteral("center")) {
46 return static_cast<int8_t>(StyleVerticalAlignKeyword::Middle
);
48 return static_cast<int8_t>(StyleVerticalAlignKeyword::Baseline
);
51 if (aAttribute
== nsGkAtoms::columnalign_
) {
52 if (aAttributeValue
.EqualsLiteral("left")) {
53 return int8_t(StyleTextAlign::Left
);
55 if (aAttributeValue
.EqualsLiteral("right")) {
56 return int8_t(StyleTextAlign::Right
);
58 return int8_t(StyleTextAlign::Center
);
61 if (aAttribute
== nsGkAtoms::rowlines_
||
62 aAttribute
== nsGkAtoms::columnlines_
) {
63 if (aAttributeValue
.EqualsLiteral("solid")) {
64 return static_cast<int8_t>(StyleBorderStyle::Solid
);
66 if (aAttributeValue
.EqualsLiteral("dashed")) {
67 return static_cast<int8_t>(StyleBorderStyle::Dashed
);
69 return static_cast<int8_t>(StyleBorderStyle::None
);
72 MOZ_CRASH("Unrecognized attribute.");
76 static nsTArray
<int8_t>* ExtractStyleValues(const nsAString
& aString
,
78 bool aAllowMultiValues
) {
79 nsTArray
<int8_t>* styleArray
= nullptr;
81 const char16_t
* start
= aString
.BeginReading();
82 const char16_t
* end
= aString
.EndReading();
84 int32_t startIndex
= 0;
88 // Skip leading spaces.
89 while ((start
< end
) && nsCRT::IsAsciiSpace(*start
)) {
94 // Look for the end of the string, or another space.
95 while ((start
< end
) && !nsCRT::IsAsciiSpace(*start
)) {
100 // Grab the value found and process it.
102 if (!styleArray
) styleArray
= new nsTArray
<int8_t>();
104 // We want to return a null array if an attribute gives multiple values,
105 // but multiple values aren't allowed.
106 if (styleArray
->Length() > 1 && !aAllowMultiValues
) {
111 nsDependentSubstring
valueString(aString
, startIndex
, count
);
112 int8_t styleValue
= ParseStyleValue(aAttribute
, valueString
);
113 styleArray
->AppendElement(styleValue
);
122 static nsresult
ReportParseError(nsIFrame
* aFrame
, const char16_t
* aAttribute
,
123 const char16_t
* aValue
) {
124 nsIContent
* content
= aFrame
->GetContent();
126 AutoTArray
<nsString
, 3> params
;
127 params
.AppendElement(aValue
);
128 params
.AppendElement(aAttribute
);
129 params
.AppendElement(nsDependentAtomString(content
->NodeInfo()->NameAtom()));
131 return nsContentUtils::ReportToConsole(
132 nsIScriptError::errorFlag
, "Layout: MathML"_ns
, content
->OwnerDoc(),
133 nsContentUtils::eMATHML_PROPERTIES
, "AttributeParsingError", params
);
136 // Each rowalign='top bottom' or columnalign='left right center' (from
137 // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
138 // stored in the property table. Row/Cell frames query the property table
139 // to see what values apply to them.
141 NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowAlignProperty
, nsTArray
<int8_t>)
142 NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowLinesProperty
, nsTArray
<int8_t>)
143 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ColumnAlignProperty
, nsTArray
<int8_t>)
144 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ColumnLinesProperty
, nsTArray
<int8_t>)
146 static const FramePropertyDescriptor
<nsTArray
<int8_t>>* AttributeToProperty(
147 nsAtom
* aAttribute
) {
148 if (aAttribute
== nsGkAtoms::rowalign_
) return RowAlignProperty();
149 if (aAttribute
== nsGkAtoms::rowlines_
) return RowLinesProperty();
150 if (aAttribute
== nsGkAtoms::columnalign_
) return ColumnAlignProperty();
151 NS_ASSERTION(aAttribute
== nsGkAtoms::columnlines_
, "Invalid attribute");
152 return ColumnLinesProperty();
155 /* This method looks for a property that applies to a cell, but it looks
156 * recursively because some cell properties can come from the cell, a row,
157 * a table, etc. This function searches through the hierarchy for a property
158 * and returns its value. The function stops searching after checking a <mtable>
161 static nsTArray
<int8_t>* FindCellProperty(
162 const nsIFrame
* aCellFrame
,
163 const FramePropertyDescriptor
<nsTArray
<int8_t>>* aFrameProperty
) {
164 const nsIFrame
* currentFrame
= aCellFrame
;
165 nsTArray
<int8_t>* propertyData
= nullptr;
167 while (currentFrame
) {
168 propertyData
= currentFrame
->GetProperty(aFrameProperty
);
169 bool frameIsTable
= (currentFrame
->IsTableFrame());
171 if (propertyData
|| frameIsTable
)
172 currentFrame
= nullptr; // A null frame pointer exits the loop
174 currentFrame
= currentFrame
->GetParent(); // Go to the parent frame
180 static void ApplyBorderToStyle(const nsMathMLmtdFrame
* aFrame
,
181 nsStyleBorder
& aStyleBorder
) {
182 uint32_t rowIndex
= aFrame
->RowIndex();
183 uint32_t columnIndex
= aFrame
->ColIndex();
185 nscoord borderWidth
= nsPresContext::CSSPixelsToAppUnits(1);
187 nsTArray
<int8_t>* rowLinesList
= FindCellProperty(aFrame
, RowLinesProperty());
189 nsTArray
<int8_t>* columnLinesList
=
190 FindCellProperty(aFrame
, ColumnLinesProperty());
192 const auto a2d
= aFrame
->PresContext()->AppUnitsPerDevPixel();
194 // We don't place a row line on top of the first row
195 if (rowIndex
> 0 && rowLinesList
) {
196 // If the row number is greater than the number of provided rowline
197 // values, we simply repeat the last value.
198 uint32_t listLength
= rowLinesList
->Length();
199 if (rowIndex
< listLength
) {
200 aStyleBorder
.SetBorderStyle(
202 static_cast<StyleBorderStyle
>(rowLinesList
->ElementAt(rowIndex
- 1)));
204 aStyleBorder
.SetBorderStyle(eSideTop
,
205 static_cast<StyleBorderStyle
>(
206 rowLinesList
->ElementAt(listLength
- 1)));
208 aStyleBorder
.SetBorderWidth(eSideTop
, borderWidth
, a2d
);
211 // We don't place a column line on the left of the first column.
212 if (columnIndex
> 0 && columnLinesList
) {
213 // If the column number is greater than the number of provided columline
214 // values, we simply repeat the last value.
215 uint32_t listLength
= columnLinesList
->Length();
216 if (columnIndex
< listLength
) {
217 aStyleBorder
.SetBorderStyle(
218 eSideLeft
, static_cast<StyleBorderStyle
>(
219 columnLinesList
->ElementAt(columnIndex
- 1)));
221 aStyleBorder
.SetBorderStyle(
222 eSideLeft
, static_cast<StyleBorderStyle
>(
223 columnLinesList
->ElementAt(listLength
- 1)));
225 aStyleBorder
.SetBorderWidth(eSideLeft
, borderWidth
, a2d
);
229 static nsMargin
ComputeBorderOverflow(nsMathMLmtdFrame
* aFrame
,
230 const nsStyleBorder
& aStyleBorder
) {
234 nsTableFrame
* table
= aFrame
->GetTableFrame();
235 aFrame
->GetCellIndexes(rowIndex
, columnIndex
);
237 overflow
.left
= table
->GetColSpacing(-1);
238 overflow
.right
= table
->GetColSpacing(0) / 2;
239 } else if (columnIndex
== table
->GetColCount() - 1) {
240 overflow
.left
= table
->GetColSpacing(columnIndex
- 1) / 2;
241 overflow
.right
= table
->GetColSpacing(columnIndex
+ 1);
243 overflow
.left
= table
->GetColSpacing(columnIndex
- 1) / 2;
244 overflow
.right
= table
->GetColSpacing(columnIndex
) / 2;
247 overflow
.top
= table
->GetRowSpacing(-1);
248 overflow
.bottom
= table
->GetRowSpacing(0) / 2;
249 } else if (rowIndex
== table
->GetRowCount() - 1) {
250 overflow
.top
= table
->GetRowSpacing(rowIndex
- 1) / 2;
251 overflow
.bottom
= table
->GetRowSpacing(rowIndex
+ 1);
253 overflow
.top
= table
->GetRowSpacing(rowIndex
- 1) / 2;
254 overflow
.bottom
= table
->GetRowSpacing(rowIndex
) / 2;
260 * A variant of the nsDisplayBorder contains special code to render a border
261 * around a nsMathMLmtdFrame based on the rowline and columnline properties
262 * set on the cell frame.
264 class nsDisplaymtdBorder final
: public nsDisplayBorder
{
266 nsDisplaymtdBorder(nsDisplayListBuilder
* aBuilder
, nsMathMLmtdFrame
* aFrame
)
267 : nsDisplayBorder(aBuilder
, aFrame
) {}
269 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
270 bool* aSnap
) const override
{
272 nsStyleBorder styleBorder
= *mFrame
->StyleBorder();
273 nsMathMLmtdFrame
* frame
= static_cast<nsMathMLmtdFrame
*>(mFrame
);
274 ApplyBorderToStyle(frame
, styleBorder
);
275 nsRect bounds
= CalculateBounds
<nsRect
>(styleBorder
);
276 nsMargin overflow
= ComputeBorderOverflow(frame
, styleBorder
);
277 bounds
.Inflate(overflow
);
281 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
282 gfxContext
* aCtx
) override
{
283 nsStyleBorder styleBorder
= *mFrame
->StyleBorder();
284 nsMathMLmtdFrame
* frame
= static_cast<nsMathMLmtdFrame
*>(mFrame
);
285 ApplyBorderToStyle(frame
, styleBorder
);
287 nsRect bounds
= nsRect(ToReferenceFrame(), mFrame
->GetSize());
288 nsMargin overflow
= ComputeBorderOverflow(frame
, styleBorder
);
289 bounds
.Inflate(overflow
);
291 PaintBorderFlags flags
= aBuilder
->ShouldSyncDecodeImages()
292 ? PaintBorderFlags::SyncDecodeImages
293 : PaintBorderFlags();
295 Unused
<< nsCSSRendering::PaintBorderWithStyleBorder(
296 mFrame
->PresContext(), *aCtx
, mFrame
, GetPaintRect(aBuilder
, aCtx
),
297 bounds
, styleBorder
, mFrame
->Style(), flags
, mFrame
->GetSkipSides());
300 bool CreateWebRenderCommands(
301 mozilla::wr::DisplayListBuilder
& aBuilder
,
302 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
303 const StackingContextHelper
& aSc
,
304 mozilla::layers::RenderRootStateManager
* aManager
,
305 nsDisplayListBuilder
* aDisplayListBuilder
) override
{
309 virtual bool IsInvisibleInRect(const nsRect
& aRect
) const override
{
315 # define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
317 mozilla::StyleDisplay::_expected == _frame->StyleDisplay()->mDisplay, \
320 # define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
323 static void ParseFrameAttribute(nsIFrame
* aFrame
, nsAtom
* aAttribute
,
324 bool aAllowMultiValues
) {
325 nsAutoString attrValue
;
327 Element
* frameElement
= aFrame
->GetContent()->AsElement();
328 frameElement
->GetAttr(aAttribute
, attrValue
);
330 if (!attrValue
.IsEmpty()) {
331 nsTArray
<int8_t>* valueList
=
332 ExtractStyleValues(attrValue
, aAttribute
, aAllowMultiValues
);
334 // If valueList is null, that indicates a problem with the attribute value.
335 // Only set properties on a valid attribute value.
337 // The code reading the property assumes that this list is nonempty.
338 NS_ASSERTION(valueList
->Length() >= 1, "valueList should not be empty!");
339 aFrame
->SetProperty(AttributeToProperty(aAttribute
), valueList
);
341 ReportParseError(aFrame
, aAttribute
->GetUTF16String(), attrValue
.get());
348 // Specifies the distance between successive rows in an mtable. Multiple
349 // lengths can be specified, each corresponding to its respective position
350 // between rows. For example:
358 // If the number of row gaps exceeds the number of lengths specified, the final
359 // specified length is repeated. Additional lengths are ignored.
364 // Unitless values are permitted and provide a multiple of the default value
365 // Negative values are forbidden.
370 // Specifies the distance between successive columns in an mtable. Multiple
371 // lengths can be specified, each corresponding to its respective position
372 // between columns. For example:
374 // [COLUMN_0] columnspace_0 [COLUMN_1] columnspace_1 [COLUMN_2]
376 // If the number of column gaps exceeds the number of lengths specified, the
377 // final specified length is repeated. Additional lengths are ignored.
382 // Unitless values are permitted and provide a multiple of the default value
383 // Negative values are forbidden.
388 // Specifies the distance between the mtable and its frame (if any). The
389 // first value specified provides the spacing between the left and right edge
390 // of the table and the frame, the second value determines the spacing between
391 // the top and bottom edges and the frame.
393 // An error is reported if only one length is passed. Any additional lengths
396 // values: length length
397 // default: 0em 0ex If frame attribute is "none" or not specified,
398 // 0.4em 0.5ex otherwise
400 // Unitless values are permitted and provide a multiple of the default value
401 // Negative values are forbidden.
404 static const float kDefaultRowspacingEx
= 1.0f
;
405 static const float kDefaultColumnspacingEm
= 0.8f
;
406 static const float kDefaultFramespacingArg0Em
= 0.4f
;
407 static const float kDefaultFramespacingArg1Ex
= 0.5f
;
409 static void ExtractSpacingValues(const nsAString
& aString
, nsAtom
* aAttribute
,
410 nsTArray
<nscoord
>& aSpacingArray
,
411 nsIFrame
* aFrame
, nscoord aDefaultValue0
,
412 nscoord aDefaultValue1
,
413 float aFontSizeInflation
) {
414 nsPresContext
* presContext
= aFrame
->PresContext();
415 ComputedStyle
* computedStyle
= aFrame
->Style();
417 const char16_t
* start
= aString
.BeginReading();
418 const char16_t
* end
= aString
.EndReading();
420 int32_t startIndex
= 0;
422 int32_t elementNum
= 0;
424 while (start
< end
) {
425 // Skip leading spaces.
426 while ((start
< end
) && nsCRT::IsAsciiSpace(*start
)) {
431 // Look for the end of the string, or another space.
432 while ((start
< end
) && !nsCRT::IsAsciiSpace(*start
)) {
437 // Grab the value found and process it.
439 const nsAString
& str
= Substring(aString
, startIndex
, count
);
440 nsAutoString valueString
;
441 valueString
.Assign(str
);
443 if (aAttribute
== nsGkAtoms::framespacing_
&& elementNum
) {
444 newValue
= aDefaultValue1
;
446 newValue
= aDefaultValue0
;
448 nsMathMLFrame::ParseNumericValue(valueString
, &newValue
, 0, presContext
,
449 computedStyle
, aFontSizeInflation
);
450 aSpacingArray
.AppendElement(newValue
);
459 static void ParseSpacingAttribute(nsMathMLmtableFrame
* aFrame
,
460 nsAtom
* aAttribute
) {
461 NS_ASSERTION(aAttribute
== nsGkAtoms::rowspacing_
||
462 aAttribute
== nsGkAtoms::columnspacing_
||
463 aAttribute
== nsGkAtoms::framespacing_
,
464 "Non spacing attribute passed");
466 nsAutoString attrValue
;
467 Element
* frameElement
= aFrame
->GetContent()->AsElement();
468 frameElement
->GetAttr(aAttribute
, attrValue
);
470 if (nsGkAtoms::framespacing_
== aAttribute
) {
472 frameElement
->GetAttr(nsGkAtoms::frame
, frame
);
473 if (frame
.IsEmpty() || frame
.EqualsLiteral("none")) {
474 aFrame
->SetFrameSpacing(0, 0);
482 float fontSizeInflation
= nsLayoutUtils::FontSizeInflationFor(aFrame
);
483 RefPtr
<nsFontMetrics
> fm
=
484 nsLayoutUtils::GetFontMetricsForFrame(aFrame
, fontSizeInflation
);
485 if (nsGkAtoms::rowspacing_
== aAttribute
) {
486 value
= kDefaultRowspacingEx
* fm
->XHeight();
488 } else if (nsGkAtoms::columnspacing_
== aAttribute
) {
489 value
= kDefaultColumnspacingEm
* fm
->EmHeight();
492 value
= kDefaultFramespacingArg0Em
* fm
->EmHeight();
493 value2
= kDefaultFramespacingArg1Ex
* fm
->XHeight();
496 nsTArray
<nscoord
> valueList
;
497 ExtractSpacingValues(attrValue
, aAttribute
, valueList
, aFrame
, value
, value2
,
499 if (valueList
.Length() == 0) {
500 if (frameElement
->HasAttr(aAttribute
)) {
501 ReportParseError(aFrame
, aAttribute
->GetUTF16String(), attrValue
.get());
503 valueList
.AppendElement(value
);
505 if (aAttribute
== nsGkAtoms::framespacing_
) {
506 if (valueList
.Length() == 1) {
507 if (frameElement
->HasAttr(aAttribute
)) {
508 ReportParseError(aFrame
, aAttribute
->GetUTF16String(), attrValue
.get());
510 valueList
.AppendElement(value2
);
511 } else if (valueList
.Length() != 2) {
512 ReportParseError(aFrame
, aAttribute
->GetUTF16String(), attrValue
.get());
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), valueList
.ElementAt(1));
525 static void ParseSpacingAttributes(nsMathMLmtableFrame
* aTableFrame
) {
526 ParseSpacingAttribute(aTableFrame
, nsGkAtoms::rowspacing_
);
527 ParseSpacingAttribute(aTableFrame
, nsGkAtoms::columnspacing_
);
528 ParseSpacingAttribute(aTableFrame
, nsGkAtoms::framespacing_
);
529 aTableFrame
->SetUseCSSSpacing();
532 // map all attributes within a table -- requires the indices of rows and cells.
533 // so it can only happen after they are made ready by the table base class.
534 static void MapAllAttributesIntoCSS(nsMathMLmtableFrame
* aTableFrame
) {
535 // Map mtable rowalign & rowlines.
536 ParseFrameAttribute(aTableFrame
, nsGkAtoms::rowalign_
, true);
537 ParseFrameAttribute(aTableFrame
, nsGkAtoms::rowlines_
, true);
539 // Map mtable columnalign & columnlines.
540 ParseFrameAttribute(aTableFrame
, nsGkAtoms::columnalign_
, true);
541 ParseFrameAttribute(aTableFrame
, nsGkAtoms::columnlines_
, true);
543 // Map mtable rowspacing, columnspacing & framespacing
544 ParseSpacingAttributes(aTableFrame
);
546 // mtable is simple and only has one (pseudo) row-group
547 nsIFrame
* rgFrame
= aTableFrame
->PrincipalChildList().FirstChild();
548 if (!rgFrame
|| !rgFrame
->IsTableRowGroupFrame()) return;
550 for (nsIFrame
* rowFrame
: rgFrame
->PrincipalChildList()) {
551 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame
, TableRow
);
552 if (rowFrame
->IsTableRowFrame()) {
554 ParseFrameAttribute(rowFrame
, nsGkAtoms::rowalign_
, false);
555 // Map row columnalign.
556 ParseFrameAttribute(rowFrame
, nsGkAtoms::columnalign_
, true);
558 for (nsIFrame
* cellFrame
: rowFrame
->PrincipalChildList()) {
559 DEBUG_VERIFY_THAT_FRAME_IS(cellFrame
, TableCell
);
560 if (cellFrame
->IsTableCellFrame()) {
561 // Map cell rowalign.
562 ParseFrameAttribute(cellFrame
, nsGkAtoms::rowalign_
, false);
563 // Map row columnalign.
564 ParseFrameAttribute(cellFrame
, nsGkAtoms::columnalign_
, false);
571 // the align attribute of mtable can have a row number which indicates
572 // from where to anchor the table, e.g., top 5 means anchor the table at
573 // the top of the 5th row, axis -1 means anchor the table on the axis of
576 // The REC says that the syntax is
577 // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*'
578 // the parsing could have been simpler with that syntax
579 // but for backward compatibility we make optional
580 // the whitespaces between the alignment name and the row number
590 static void ParseAlignAttribute(nsString
& aValue
, eAlign
& aAlign
,
591 int32_t& aRowIndex
) {
592 // by default, the table is centered about the axis
594 aAlign
= eAlign_axis
;
597 // we only have to remove the leading spaces because
598 // ToInteger ignores the whitespaces around the number
599 aValue
.CompressWhitespace(true, false);
601 if (0 == aValue
.Find(u
"top")) {
602 len
= 3; // 3 is the length of 'top'
604 } else if (0 == aValue
.Find(u
"bottom")) {
605 len
= 6; // 6 is the length of 'bottom'
606 aAlign
= eAlign_bottom
;
607 } else if (0 == aValue
.Find(u
"center")) {
608 len
= 6; // 6 is the length of 'center'
609 aAlign
= eAlign_center
;
610 } else if (0 == aValue
.Find(u
"baseline")) {
611 len
= 8; // 8 is the length of 'baseline'
612 aAlign
= eAlign_baseline
;
613 } else if (0 == aValue
.Find(u
"axis")) {
614 len
= 4; // 4 is the length of 'axis'
615 aAlign
= eAlign_axis
;
619 aValue
.Cut(0, len
); // aValue is not a const here
620 aRowIndex
= aValue
.ToInteger(&error
);
621 if (NS_FAILED(error
)) aRowIndex
= 0;
626 // call ListMathMLTree(mParent) to get the big picture
627 static void ListMathMLTree(nsIFrame
* atLeast
) {
628 // climb up to <math> or <body> if <math> isn't there
629 nsIFrame
* f
= atLeast
;
630 for (; f
; f
= f
->GetParent()) {
631 nsIContent
* c
= f
->GetContent();
632 if (!c
|| c
->IsMathMLElement(nsGkAtoms::math
) ||
633 // XXXbaku which kind of body tag?
634 c
->NodeInfo()->NameAtom(nsGkAtoms::body
))
643 // implementation of nsMathMLmtableWrapperFrame
645 NS_QUERYFRAME_HEAD(nsMathMLmtableWrapperFrame
)
646 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
647 NS_QUERYFRAME_TAIL_INHERITING(nsTableWrapperFrame
)
649 nsContainerFrame
* NS_NewMathMLmtableOuterFrame(PresShell
* aPresShell
,
650 ComputedStyle
* aStyle
) {
651 return new (aPresShell
)
652 nsMathMLmtableWrapperFrame(aStyle
, aPresShell
->GetPresContext());
655 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableWrapperFrame
)
657 nsMathMLmtableWrapperFrame::~nsMathMLmtableWrapperFrame() = default;
659 nsresult
nsMathMLmtableWrapperFrame::AttributeChanged(int32_t aNameSpaceID
,
662 // Attributes specific to <mtable>:
663 // frame : in mathml.css
664 // framespacing : here
665 // groupalign : not yet supported
666 // equalrows : not yet supported
667 // equalcolumns : not yet supported
668 // displaystyle : here and in mathml.css
673 // columnalign : here
674 // columnlines : here
675 // columnspacing : here
677 // mtable is simple and only has one (pseudo) row-group inside our inner-table
678 nsIFrame
* tableFrame
= mFrames
.FirstChild();
679 NS_ASSERTION(tableFrame
&& tableFrame
->IsTableFrame(),
680 "should always have an inner table frame");
681 nsIFrame
* rgFrame
= tableFrame
->PrincipalChildList().FirstChild();
682 if (!rgFrame
|| !rgFrame
->IsTableRowGroupFrame()) return NS_OK
;
684 // align - just need to issue a dirty (resize) reflow command
685 if (aAttribute
== nsGkAtoms::align
) {
686 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::None
,
691 // displaystyle - may seem innocuous, but it is actually very harsh --
692 // like changing an unit. Blow away and recompute all our automatic
693 // presentational data, and issue a style-changed reflow request
694 if (aAttribute
== nsGkAtoms::displaystyle_
) {
695 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(GetParent());
696 // Need to reflow the parent, not us, because this can actually
698 PresShell()->FrameNeedsReflow(GetParent(),
699 IntrinsicDirty::FrameAncestorsAndDescendants
,
704 // ...and the other attributes affect rows or columns in one way or another
706 if (aAttribute
== nsGkAtoms::rowspacing_
||
707 aAttribute
== nsGkAtoms::columnspacing_
||
708 aAttribute
== nsGkAtoms::framespacing_
) {
709 nsMathMLmtableFrame
* mathMLmtableFrame
= do_QueryFrame(tableFrame
);
710 if (mathMLmtableFrame
) {
711 ParseSpacingAttribute(mathMLmtableFrame
, aAttribute
);
712 mathMLmtableFrame
->SetUseCSSSpacing();
714 } else if (aAttribute
== nsGkAtoms::rowalign_
||
715 aAttribute
== nsGkAtoms::rowlines_
||
716 aAttribute
== nsGkAtoms::columnalign_
||
717 aAttribute
== nsGkAtoms::columnlines_
) {
718 // clear any cached property list for this table
719 tableFrame
->RemoveProperty(AttributeToProperty(aAttribute
));
720 // Reparse the new attribute on the table.
721 ParseFrameAttribute(tableFrame
, aAttribute
, true);
723 // Ignore attributes that do not affect layout.
727 // Explicitly request a reflow in our subtree to pick up any changes
728 PresShell()->FrameNeedsReflow(
729 this, IntrinsicDirty::FrameAncestorsAndDescendants
, NS_FRAME_IS_DIRTY
);
734 nsIFrame
* nsMathMLmtableWrapperFrame::GetRowFrameAt(int32_t aRowIndex
) {
735 int32_t rowCount
= GetRowCount();
737 // Negative indices mean to find upwards from the end.
739 aRowIndex
= rowCount
+ aRowIndex
;
741 // aRowIndex is 1-based, so convert it to a 0-based index
745 // if our inner table says that the index is valid, find the row now
746 if (0 <= aRowIndex
&& aRowIndex
<= rowCount
) {
747 nsIFrame
* tableFrame
= mFrames
.FirstChild();
748 NS_ASSERTION(tableFrame
&& tableFrame
->IsTableFrame(),
749 "should always have an inner table frame");
750 nsIFrame
* rgFrame
= tableFrame
->PrincipalChildList().FirstChild();
751 if (!rgFrame
|| !rgFrame
->IsTableRowGroupFrame()) return nullptr;
752 for (nsIFrame
* rowFrame
: rgFrame
->PrincipalChildList()) {
753 if (aRowIndex
== 0) {
754 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame
, TableRow
);
755 if (!rowFrame
->IsTableRowFrame()) return nullptr;
765 void nsMathMLmtableWrapperFrame::Reflow(nsPresContext
* aPresContext
,
766 ReflowOutput
& aDesiredSize
,
767 const ReflowInput
& aReflowInput
,
768 nsReflowStatus
& aStatus
) {
769 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
772 // we want to return a table that is anchored according to the align attribute
774 nsTableWrapperFrame::Reflow(aPresContext
, aDesiredSize
, aReflowInput
,
776 NS_ASSERTION(aDesiredSize
.Height() >= 0, "illegal height for mtable");
777 NS_ASSERTION(aDesiredSize
.Width() >= 0, "illegal width for mtable");
779 // see if the user has set the align attribute on the <mtable>
780 int32_t rowIndex
= 0;
781 eAlign tableAlign
= eAlign_axis
;
782 mContent
->AsElement()->GetAttr(nsGkAtoms::align
, value
);
783 if (!value
.IsEmpty()) {
784 ParseAlignAttribute(value
, tableAlign
, rowIndex
);
787 // adjustments if there is a specified row from where to anchor the table
788 // (conceptually: when there is no row of reference, picture the table as if
789 // it is wrapped in a single big fictional row at dy = 0, this way of
790 // doing so allows us to have a single code path for all cases).
792 WritingMode wm
= aDesiredSize
.GetWritingMode();
793 nscoord blockSize
= aDesiredSize
.BSize(wm
);
794 nsIFrame
* rowFrame
= nullptr;
796 rowFrame
= GetRowFrameAt(rowIndex
);
798 // translate the coordinates to be relative to us and in our writing mode
799 nsIFrame
* frame
= rowFrame
;
800 LogicalRect
rect(wm
, frame
->GetRect(),
801 aReflowInput
.ComputedSizeAsContainerIfConstrained());
802 blockSize
= rect
.BSize(wm
);
804 nsIFrame
* parent
= frame
->GetParent();
805 dy
+= frame
->BStart(wm
, parent
->GetSize());
807 } while (frame
!= this);
810 switch (tableAlign
) {
812 aDesiredSize
.SetBlockStartAscent(dy
);
815 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
);
818 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
/ 2);
820 case eAlign_baseline
:
822 // anchor the table on the baseline of the row of reference
823 nscoord rowAscent
= ((nsTableRowFrame
*)rowFrame
)->GetMaxCellAscent();
824 if (rowAscent
) { // the row has at least one cell with 'vertical-align:
826 aDesiredSize
.SetBlockStartAscent(dy
+ rowAscent
);
830 // in other situations, fallback to center
831 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
/ 2);
835 // XXX should instead use style data from the row of reference here ?
836 RefPtr
<nsFontMetrics
> fm
=
837 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
839 GetAxisHeight(aReflowInput
.mRenderingContext
->GetDrawTarget(), fm
,
842 // anchor the table on the axis of the row of reference
843 // XXX fallback to baseline because it is a hard problem
844 // XXX need to fetch the axis of the row; would need rowalign=axis to
846 nscoord rowAscent
= ((nsTableRowFrame
*)rowFrame
)->GetMaxCellAscent();
847 if (rowAscent
) { // the row has at least one cell with 'vertical-align:
849 aDesiredSize
.SetBlockStartAscent(dy
+ rowAscent
);
853 // in other situations, fallback to using half of the height
854 aDesiredSize
.SetBlockStartAscent(dy
+ blockSize
/ 2 + axisHeight
);
859 mReference
.y
= aDesiredSize
.BlockStartAscent();
861 // just make-up a bounding metrics
862 mBoundingMetrics
= nsBoundingMetrics();
863 mBoundingMetrics
.ascent
= aDesiredSize
.BlockStartAscent();
864 mBoundingMetrics
.descent
=
865 aDesiredSize
.Height() - aDesiredSize
.BlockStartAscent();
866 mBoundingMetrics
.width
= aDesiredSize
.Width();
867 mBoundingMetrics
.leftBearing
= 0;
868 mBoundingMetrics
.rightBearing
= aDesiredSize
.Width();
870 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
873 nsContainerFrame
* NS_NewMathMLmtableFrame(PresShell
* aPresShell
,
874 ComputedStyle
* aStyle
) {
875 return new (aPresShell
)
876 nsMathMLmtableFrame(aStyle
, aPresShell
->GetPresContext());
879 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame
)
881 nsMathMLmtableFrame::~nsMathMLmtableFrame() = default;
883 void nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID
,
884 nsFrameList
&& aChildList
) {
885 nsTableFrame::SetInitialChildList(aListID
, std::move(aChildList
));
886 MapAllAttributesIntoCSS(this);
889 void nsMathMLmtableFrame::RestyleTable() {
890 // re-sync MathML specific style data that may have changed
891 MapAllAttributesIntoCSS(this);
893 // Explicitly request a re-resolve and reflow in our subtree to pick up any
895 PresContext()->RestyleManager()->PostRestyleEvent(
896 mContent
->AsElement(), RestyleHint::RestyleSubtree(),
897 nsChangeHint_AllReflowHints
);
900 nscoord
nsMathMLmtableFrame::GetColSpacing(int32_t aColIndex
) {
901 if (mUseCSSSpacing
) {
902 return nsTableFrame::GetColSpacing(aColIndex
);
904 if (!mColSpacing
.Length()) {
905 NS_ERROR("mColSpacing should not be empty");
908 if (aColIndex
< 0 || aColIndex
>= GetColCount()) {
909 NS_ASSERTION(aColIndex
== -1 || aColIndex
== GetColCount(),
910 "Desired column beyond bounds of table and border");
911 return mFrameSpacingX
;
913 if ((uint32_t)aColIndex
>= mColSpacing
.Length()) {
914 return mColSpacing
.LastElement();
916 return mColSpacing
.ElementAt(aColIndex
);
919 nscoord
nsMathMLmtableFrame::GetColSpacing(int32_t aStartColIndex
,
920 int32_t aEndColIndex
) {
921 if (mUseCSSSpacing
) {
922 return nsTableFrame::GetColSpacing(aStartColIndex
, aEndColIndex
);
924 if (aStartColIndex
== aEndColIndex
) {
927 if (!mColSpacing
.Length()) {
928 NS_ERROR("mColSpacing should not be empty");
932 if (aStartColIndex
< 0) {
933 NS_ASSERTION(aStartColIndex
== -1,
934 "Desired column beyond bounds of table and border");
935 space
+= mFrameSpacingX
;
938 if (aEndColIndex
>= GetColCount()) {
939 NS_ASSERTION(aEndColIndex
== GetColCount(),
940 "Desired column beyond bounds of table and border");
941 space
+= mFrameSpacingX
;
942 aEndColIndex
= GetColCount();
944 // Only iterate over column spacing when there is the potential to vary
945 int32_t min
= std::min(aEndColIndex
, (int32_t)mColSpacing
.Length());
946 for (int32_t i
= aStartColIndex
; i
< min
; i
++) {
947 space
+= mColSpacing
.ElementAt(i
);
949 // The remaining values are constant. Note that if there are more
950 // column spacings specified than there are columns, LastElement() will be
951 // multiplied by 0, so it is still safe to use.
952 space
+= (aEndColIndex
- min
) * mColSpacing
.LastElement();
956 nscoord
nsMathMLmtableFrame::GetRowSpacing(int32_t aRowIndex
) {
957 if (mUseCSSSpacing
) {
958 return nsTableFrame::GetRowSpacing(aRowIndex
);
960 if (!mRowSpacing
.Length()) {
961 NS_ERROR("mRowSpacing should not be empty");
964 if (aRowIndex
< 0 || aRowIndex
>= GetRowCount()) {
965 NS_ASSERTION(aRowIndex
== -1 || aRowIndex
== GetRowCount(),
966 "Desired row beyond bounds of table and border");
967 return mFrameSpacingY
;
969 if ((uint32_t)aRowIndex
>= mRowSpacing
.Length()) {
970 return mRowSpacing
.LastElement();
972 return mRowSpacing
.ElementAt(aRowIndex
);
975 nscoord
nsMathMLmtableFrame::GetRowSpacing(int32_t aStartRowIndex
,
976 int32_t aEndRowIndex
) {
977 if (mUseCSSSpacing
) {
978 return nsTableFrame::GetRowSpacing(aStartRowIndex
, aEndRowIndex
);
980 if (aStartRowIndex
== aEndRowIndex
) {
983 if (!mRowSpacing
.Length()) {
984 NS_ERROR("mRowSpacing should not be empty");
988 if (aStartRowIndex
< 0) {
989 NS_ASSERTION(aStartRowIndex
== -1,
990 "Desired row beyond bounds of table and border");
991 space
+= mFrameSpacingY
;
994 if (aEndRowIndex
>= GetRowCount()) {
995 NS_ASSERTION(aEndRowIndex
== GetRowCount(),
996 "Desired row beyond bounds of table and border");
997 space
+= mFrameSpacingY
;
998 aEndRowIndex
= GetRowCount();
1000 // Only iterate over row spacing when there is the potential to vary
1001 int32_t min
= std::min(aEndRowIndex
, (int32_t)mRowSpacing
.Length());
1002 for (int32_t i
= aStartRowIndex
; i
< min
; i
++) {
1003 space
+= mRowSpacing
.ElementAt(i
);
1005 // The remaining values are constant. Note that if there are more
1006 // row spacings specified than there are row, LastElement() will be
1007 // multiplied by 0, so it is still safe to use.
1008 space
+= (aEndRowIndex
- min
) * mRowSpacing
.LastElement();
1012 void nsMathMLmtableFrame::SetUseCSSSpacing() {
1013 mUseCSSSpacing
= !(mContent
->AsElement()->HasAttr(nsGkAtoms::rowspacing_
) ||
1014 mContent
->AsElement()->HasAttr(
1015 kNameSpaceID_None
, nsGkAtoms::columnspacing_
) ||
1016 mContent
->AsElement()->HasAttr(nsGkAtoms::framespacing_
));
1019 NS_QUERYFRAME_HEAD(nsMathMLmtableFrame
)
1020 NS_QUERYFRAME_ENTRY(nsMathMLmtableFrame
)
1021 NS_QUERYFRAME_TAIL_INHERITING(nsTableFrame
)
1024 // implementation of nsMathMLmtrFrame
1026 nsContainerFrame
* NS_NewMathMLmtrFrame(PresShell
* aPresShell
,
1027 ComputedStyle
* aStyle
) {
1028 return new (aPresShell
)
1029 nsMathMLmtrFrame(aStyle
, aPresShell
->GetPresContext());
1032 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame
)
1034 nsMathMLmtrFrame::~nsMathMLmtrFrame() = default;
1036 nsresult
nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID
,
1039 // Attributes specific to <mtr>:
1040 // groupalign : Not yet supported.
1042 // columnalign : Here
1044 if (aAttribute
!= nsGkAtoms::rowalign_
&&
1045 aAttribute
!= nsGkAtoms::columnalign_
) {
1049 RemoveProperty(AttributeToProperty(aAttribute
));
1051 bool allowMultiValues
= (aAttribute
== nsGkAtoms::columnalign_
);
1053 // Reparse the new attribute.
1054 ParseFrameAttribute(this, aAttribute
, allowMultiValues
);
1056 // Explicitly request a reflow in our subtree to pick up any changes
1057 PresShell()->FrameNeedsReflow(
1058 this, IntrinsicDirty::FrameAncestorsAndDescendants
, NS_FRAME_IS_DIRTY
);
1064 // implementation of nsMathMLmtdFrame
1066 nsContainerFrame
* NS_NewMathMLmtdFrame(PresShell
* aPresShell
,
1067 ComputedStyle
* aStyle
,
1068 nsTableFrame
* aTableFrame
) {
1069 return new (aPresShell
) nsMathMLmtdFrame(aStyle
, aTableFrame
);
1072 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame
)
1074 nsMathMLmtdFrame::~nsMathMLmtdFrame() = default;
1076 void nsMathMLmtdFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
1077 nsIFrame
* aPrevInFlow
) {
1078 nsTableCellFrame::Init(aContent
, aParent
, aPrevInFlow
);
1080 // We want to use the ancestor <math> element's font inflation to avoid
1081 // individual cells having their own varying font inflation.
1082 RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
1085 nsresult
nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID
,
1088 // Attributes specific to <mtd>:
1089 // groupalign : Not yet supported
1091 // columnalign : here
1093 // columnspan : here
1095 if (aAttribute
== nsGkAtoms::rowalign_
||
1096 aAttribute
== nsGkAtoms::columnalign_
) {
1097 RemoveProperty(AttributeToProperty(aAttribute
));
1099 // Reparse the attribute.
1100 ParseFrameAttribute(this, aAttribute
, false);
1104 if (aAttribute
== nsGkAtoms::rowspan
||
1105 aAttribute
== nsGkAtoms::columnspan_
) {
1106 // use the naming expected by the base class
1107 if (aAttribute
== nsGkAtoms::columnspan_
) aAttribute
= nsGkAtoms::colspan
;
1108 return nsTableCellFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
1115 StyleVerticalAlignKeyword
nsMathMLmtdFrame::GetVerticalAlign() const {
1116 // Set the default alignment in case no alignment was specified
1117 auto alignment
= nsTableCellFrame::GetVerticalAlign();
1119 nsTArray
<int8_t>* alignmentList
= FindCellProperty(this, RowAlignProperty());
1121 if (alignmentList
) {
1122 uint32_t rowIndex
= RowIndex();
1124 // If the row number is greater than the number of provided rowalign values,
1125 // we simply repeat the last value.
1126 return static_cast<StyleVerticalAlignKeyword
>(
1127 (rowIndex
< alignmentList
->Length())
1128 ? alignmentList
->ElementAt(rowIndex
)
1129 : alignmentList
->LastElement());
1135 void nsMathMLmtdFrame::ProcessBorders(nsTableFrame
* aFrame
,
1136 nsDisplayListBuilder
* aBuilder
,
1137 const nsDisplayListSet
& aLists
) {
1138 aLists
.BorderBackground()->AppendNewToTop
<nsDisplaymtdBorder
>(aBuilder
, this);
1141 LogicalMargin
nsMathMLmtdFrame::GetBorderWidth(WritingMode aWM
) const {
1142 nsStyleBorder styleBorder
= *StyleBorder();
1143 ApplyBorderToStyle(this, styleBorder
);
1144 return LogicalMargin(aWM
, styleBorder
.GetComputedBorder());
1147 nsMargin
nsMathMLmtdFrame::GetBorderOverflow() {
1148 nsStyleBorder styleBorder
= *StyleBorder();
1149 ApplyBorderToStyle(this, styleBorder
);
1150 nsMargin overflow
= ComputeBorderOverflow(this, styleBorder
);
1155 // implementation of nsMathMLmtdInnerFrame
1157 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame
)
1158 NS_QUERYFRAME_ENTRY(nsIMathMLFrame
)
1159 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
1161 nsContainerFrame
* NS_NewMathMLmtdInnerFrame(PresShell
* aPresShell
,
1162 ComputedStyle
* aStyle
) {
1163 return new (aPresShell
)
1164 nsMathMLmtdInnerFrame(aStyle
, aPresShell
->GetPresContext());
1167 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame
)
1169 nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(ComputedStyle
* aStyle
,
1170 nsPresContext
* aPresContext
)
1171 : nsBlockFrame(aStyle
, aPresContext
, kClassID
)
1172 // Make a copy of the parent nsStyleText for later modification.
1174 mUniqueStyleText(MakeUnique
<nsStyleText
>(*StyleText())) {}
1176 void nsMathMLmtdInnerFrame::Reflow(nsPresContext
* aPresContext
,
1177 ReflowOutput
& aDesiredSize
,
1178 const ReflowInput
& aReflowInput
,
1179 nsReflowStatus
& aStatus
) {
1180 // Let the base class do the reflow
1181 nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, aReflowInput
, aStatus
);
1183 // more about <maligngroup/> and <malignmark/> later
1187 const nsStyleText
* nsMathMLmtdInnerFrame::StyleTextForLineLayout() {
1188 // Set the default alignment in case nothing was specified
1189 auto alignment
= uint8_t(StyleText()->mTextAlign
);
1191 nsTArray
<int8_t>* alignmentList
=
1192 FindCellProperty(this, ColumnAlignProperty());
1194 if (alignmentList
) {
1195 nsMathMLmtdFrame
* cellFrame
= (nsMathMLmtdFrame
*)GetParent();
1196 uint32_t columnIndex
= cellFrame
->ColIndex();
1198 // If the column number is greater than the number of provided columalign
1199 // values, we simply repeat the last value.
1200 if (columnIndex
< alignmentList
->Length())
1201 alignment
= alignmentList
->ElementAt(columnIndex
);
1203 alignment
= alignmentList
->ElementAt(alignmentList
->Length() - 1);
1206 mUniqueStyleText
->mTextAlign
= StyleTextAlign(alignment
);
1207 return mUniqueStyleText
.get();
1211 void nsMathMLmtdInnerFrame::DidSetComputedStyle(
1212 ComputedStyle
* aOldComputedStyle
) {
1213 nsBlockFrame::DidSetComputedStyle(aOldComputedStyle
);
1214 mUniqueStyleText
= MakeUnique
<nsStyleText
>(*StyleText());