Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / layout / mathml / nsMathMLmtableFrame.cpp
blob77f132b3426639a117dd3e0afdc4cc5a56d6eaf1
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"
15 #include "nsCRT.h"
16 #include "nsTArray.h"
17 #include "nsTableFrame.h"
18 #include "celldata.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/RestyleManager.h"
22 #include <algorithm>
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.");
73 return -1;
76 static nsTArray<int8_t>* ExtractStyleValues(const nsAString& aString,
77 nsAtom* aAttribute,
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;
85 int32_t count = 0;
87 while (start < end) {
88 // Skip leading spaces.
89 while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
90 start++;
91 startIndex++;
94 // Look for the end of the string, or another space.
95 while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
96 start++;
97 count++;
100 // Grab the value found and process it.
101 if (count > 0) {
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) {
107 delete styleArray;
108 return nullptr;
111 nsDependentSubstring valueString(aString, startIndex, count);
112 int8_t styleValue = ParseStyleValue(aAttribute, valueString);
113 styleArray->AppendElement(styleValue);
115 startIndex += count;
116 count = 0;
119 return styleArray;
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>
159 * frame.
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
173 else
174 currentFrame = currentFrame->GetParent(); // Go to the parent frame
177 return propertyData;
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(
201 eSideTop,
202 static_cast<StyleBorderStyle>(rowLinesList->ElementAt(rowIndex - 1)));
203 } else {
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)));
220 } else {
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) {
231 nsMargin overflow;
232 int32_t rowIndex;
233 int32_t columnIndex;
234 nsTableFrame* table = aFrame->GetTableFrame();
235 aFrame->GetCellIndexes(rowIndex, columnIndex);
236 if (!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);
242 } else {
243 overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
244 overflow.right = table->GetColSpacing(columnIndex) / 2;
246 if (!rowIndex) {
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);
252 } else {
253 overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
254 overflow.bottom = table->GetRowSpacing(rowIndex) / 2;
256 return overflow;
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 {
265 public:
266 nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
267 : nsDisplayBorder(aBuilder, aFrame) {}
269 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
270 bool* aSnap) const override {
271 *aSnap = true;
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);
278 return bounds;
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 {
306 return false;
309 virtual bool IsInvisibleInRect(const nsRect& aRect) const override {
310 return false;
314 #ifdef DEBUG
315 # define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
316 MOZ_ASSERT( \
317 mozilla::StyleDisplay::_expected == _frame->StyleDisplay()->mDisplay, \
318 "internal error");
319 #else
320 # define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
321 #endif
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.
336 if (valueList) {
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);
340 } else {
341 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
346 // rowspacing
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:
352 // [ROW_0]
353 // rowspace_0
354 // [ROW_1]
355 // rowspace_1
356 // [ROW_2]
358 // If the number of row gaps exceeds the number of lengths specified, the final
359 // specified length is repeated. Additional lengths are ignored.
361 // values: (length)+
362 // default: 1.0ex
364 // Unitless values are permitted and provide a multiple of the default value
365 // Negative values are forbidden.
368 // columnspacing
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.
379 // values: (length)+
380 // default: 0.8em
382 // Unitless values are permitted and provide a multiple of the default value
383 // Negative values are forbidden.
386 // framespacing
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
394 // are ignored
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;
421 int32_t count = 0;
422 int32_t elementNum = 0;
424 while (start < end) {
425 // Skip leading spaces.
426 while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
427 start++;
428 startIndex++;
431 // Look for the end of the string, or another space.
432 while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
433 start++;
434 count++;
437 // Grab the value found and process it.
438 if (count > 0) {
439 const nsAString& str = Substring(aString, startIndex, count);
440 nsAutoString valueString;
441 valueString.Assign(str);
442 nscoord newValue;
443 if (aAttribute == nsGkAtoms::framespacing_ && elementNum) {
444 newValue = aDefaultValue1;
445 } else {
446 newValue = aDefaultValue0;
448 nsMathMLFrame::ParseNumericValue(valueString, &newValue, 0, presContext,
449 computedStyle, aFontSizeInflation);
450 aSpacingArray.AppendElement(newValue);
452 startIndex += count;
453 count = 0;
454 elementNum++;
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) {
471 nsAutoString frame;
472 frameElement->GetAttr(nsGkAtoms::frame, frame);
473 if (frame.IsEmpty() || frame.EqualsLiteral("none")) {
474 aFrame->SetFrameSpacing(0, 0);
475 return;
479 nscoord value;
480 nscoord value2;
481 // Set defaults
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();
487 value2 = 0;
488 } else if (nsGkAtoms::columnspacing_ == aAttribute) {
489 value = kDefaultColumnspacingEm * fm->EmHeight();
490 value2 = 0;
491 } else {
492 value = kDefaultFramespacingArg0Em * fm->EmHeight();
493 value2 = kDefaultFramespacingArg1Ex * fm->XHeight();
496 nsTArray<nscoord> valueList;
497 ExtractSpacingValues(attrValue, aAttribute, valueList, aFrame, value, value2,
498 fontSizeInflation);
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);
520 } else {
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()) {
553 // Map row rowalign.
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
574 // the last row
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
582 enum eAlign {
583 eAlign_top,
584 eAlign_bottom,
585 eAlign_center,
586 eAlign_baseline,
587 eAlign_axis
590 static void ParseAlignAttribute(nsString& aValue, eAlign& aAlign,
591 int32_t& aRowIndex) {
592 // by default, the table is centered about the axis
593 aRowIndex = 0;
594 aAlign = eAlign_axis;
595 int32_t len = 0;
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'
603 aAlign = eAlign_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;
617 if (len) {
618 nsresult error;
619 aValue.Cut(0, len); // aValue is not a const here
620 aRowIndex = aValue.ToInteger(&error);
621 if (NS_FAILED(error)) aRowIndex = 0;
625 #ifdef DEBUG_rbs_off
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))
635 break;
637 if (!f) f = atLeast;
638 f->List(stdout, 0);
640 #endif
642 // --------
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,
660 nsAtom* aAttribute,
661 int32_t aModType) {
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
669 // align : in reflow
670 // rowalign : here
671 // rowlines : here
672 // rowspacing : here
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,
687 NS_FRAME_IS_DIRTY);
688 return NS_OK;
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
697 // affect siblings.
698 PresShell()->FrameNeedsReflow(GetParent(),
699 IntrinsicDirty::FrameAncestorsAndDescendants,
700 NS_FRAME_IS_DIRTY);
701 return NS_OK;
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);
722 } else {
723 // Ignore attributes that do not affect layout.
724 return NS_OK;
727 // Explicitly request a reflow in our subtree to pick up any changes
728 PresShell()->FrameNeedsReflow(
729 this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
731 return NS_OK;
734 nsIFrame* nsMathMLmtableWrapperFrame::GetRowFrameAt(int32_t aRowIndex) {
735 int32_t rowCount = GetRowCount();
737 // Negative indices mean to find upwards from the end.
738 if (aRowIndex < 0) {
739 aRowIndex = rowCount + aRowIndex;
740 } else {
741 // aRowIndex is 1-based, so convert it to a 0-based index
742 --aRowIndex;
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;
757 return rowFrame;
759 --aRowIndex;
762 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!");
771 nsAutoString value;
772 // we want to return a table that is anchored according to the align attribute
774 nsTableWrapperFrame::Reflow(aPresContext, aDesiredSize, aReflowInput,
775 aStatus);
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).
791 nscoord dy = 0;
792 WritingMode wm = aDesiredSize.GetWritingMode();
793 nscoord blockSize = aDesiredSize.BSize(wm);
794 nsIFrame* rowFrame = nullptr;
795 if (rowIndex) {
796 rowFrame = GetRowFrameAt(rowIndex);
797 if (rowFrame) {
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);
803 do {
804 nsIFrame* parent = frame->GetParent();
805 dy += frame->BStart(wm, parent->GetSize());
806 frame = parent;
807 } while (frame != this);
810 switch (tableAlign) {
811 case eAlign_top:
812 aDesiredSize.SetBlockStartAscent(dy);
813 break;
814 case eAlign_bottom:
815 aDesiredSize.SetBlockStartAscent(dy + blockSize);
816 break;
817 case eAlign_center:
818 aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
819 break;
820 case eAlign_baseline:
821 if (rowFrame) {
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:
825 // baseline'
826 aDesiredSize.SetBlockStartAscent(dy + rowAscent);
827 break;
830 // in other situations, fallback to center
831 aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
832 break;
833 case eAlign_axis:
834 default: {
835 // XXX should instead use style data from the row of reference here ?
836 RefPtr<nsFontMetrics> fm =
837 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
838 nscoord axisHeight;
839 GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm,
840 axisHeight);
841 if (rowFrame) {
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
845 // work better
846 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
847 if (rowAscent) { // the row has at least one cell with 'vertical-align:
848 // baseline'
849 aDesiredSize.SetBlockStartAscent(dy + rowAscent);
850 break;
853 // in other situations, fallback to using half of the height
854 aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight);
858 mReference.x = 0;
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
894 // changes
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");
906 return 0;
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) {
925 return 0;
927 if (!mColSpacing.Length()) {
928 NS_ERROR("mColSpacing should not be empty");
929 return 0;
931 nscoord space = 0;
932 if (aStartColIndex < 0) {
933 NS_ASSERTION(aStartColIndex == -1,
934 "Desired column beyond bounds of table and border");
935 space += mFrameSpacingX;
936 aStartColIndex = 0;
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();
953 return space;
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");
962 return 0;
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) {
981 return 0;
983 if (!mRowSpacing.Length()) {
984 NS_ERROR("mRowSpacing should not be empty");
985 return 0;
987 nscoord space = 0;
988 if (aStartRowIndex < 0) {
989 NS_ASSERTION(aStartRowIndex == -1,
990 "Desired row beyond bounds of table and border");
991 space += mFrameSpacingY;
992 aStartRowIndex = 0;
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();
1009 return space;
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)
1023 // --------
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,
1037 nsAtom* aAttribute,
1038 int32_t aModType) {
1039 // Attributes specific to <mtr>:
1040 // groupalign : Not yet supported.
1041 // rowalign : Here
1042 // columnalign : Here
1044 if (aAttribute != nsGkAtoms::rowalign_ &&
1045 aAttribute != nsGkAtoms::columnalign_) {
1046 return NS_OK;
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);
1060 return NS_OK;
1063 // --------
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,
1086 nsAtom* aAttribute,
1087 int32_t aModType) {
1088 // Attributes specific to <mtd>:
1089 // groupalign : Not yet supported
1090 // rowalign : here
1091 // columnalign : here
1092 // rowspan : 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);
1101 return NS_OK;
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,
1109 aModType);
1112 return NS_OK;
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());
1132 return alignment;
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);
1151 return overflow;
1154 // --------
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
1184 // ...
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);
1202 else
1203 alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
1206 mUniqueStyleText->mTextAlign = StyleTextAlign(alignment);
1207 return mUniqueStyleText.get();
1210 /* virtual */
1211 void nsMathMLmtdInnerFrame::DidSetComputedStyle(
1212 ComputedStyle* aOldComputedStyle) {
1213 nsBlockFrame::DidSetComputedStyle(aOldComputedStyle);
1214 mUniqueStyleText = MakeUnique<nsStyleText>(*StyleText());