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/. */
6
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"
14
15 #include "nsCRT.h"
16 #include "nsTArray.h"
17 #include "nsTableFrame.h"
18 #include "celldata.h"
19
20 #include "mozilla/PresShell.h"
21 #include "mozilla/RestyleManager.h"
22 #include <algorithm>
23
24 #include "nsIScriptError.h"
25 #include "nsContentUtils.h"
26 #include "nsLayoutUtils.h"
27
28 using namespace mozilla;
29 using namespace mozilla::image;
30 using mozilla::dom::Element;
31
32 //
33 // <mtable> -- table or matrix - implementation
34 //
35
ParseStyleValue(nsAtom * aAttribute,const nsAString & aAttributeValue)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);
41 }
42 if (aAttributeValue.EqualsLiteral("bottom")) {
43 return static_cast<int8_t>(StyleVerticalAlignKeyword::Bottom);
44 }
45 if (aAttributeValue.EqualsLiteral("center")) {
46 return static_cast<int8_t>(StyleVerticalAlignKeyword::Middle);
47 }
48 return static_cast<int8_t>(StyleVerticalAlignKeyword::Baseline);
49 }
50
51 if (aAttribute == nsGkAtoms::columnalign_) {
52 if (aAttributeValue.EqualsLiteral("left")) {
53 return int8_t(StyleTextAlign::Left);
54 }
55 if (aAttributeValue.EqualsLiteral("right")) {
56 return int8_t(StyleTextAlign::Right);
57 }
58 return int8_t(StyleTextAlign::Center);
59 }
60
61 if (aAttribute == nsGkAtoms::rowlines_ ||
62 aAttribute == nsGkAtoms::columnlines_) {
63 if (aAttributeValue.EqualsLiteral("solid")) {
64 return static_cast<int8_t>(StyleBorderStyle::Solid);
65 }
66 if (aAttributeValue.EqualsLiteral("dashed")) {
67 return static_cast<int8_t>(StyleBorderStyle::Dashed);
68 }
69 return static_cast<int8_t>(StyleBorderStyle::None);
70 }
71
72 MOZ_CRASH("Unrecognized attribute.");
73 return -1;
74 }
75
ExtractStyleValues(const nsAString & aString,nsAtom * aAttribute,bool aAllowMultiValues)76 static nsTArray<int8_t>* ExtractStyleValues(const nsAString& aString,
77 nsAtom* aAttribute,
78 bool aAllowMultiValues) {
79 nsTArray<int8_t>* styleArray = nullptr;
80
81 const char16_t* start = aString.BeginReading();
82 const char16_t* end = aString.EndReading();
83
84 int32_t startIndex = 0;
85 int32_t count = 0;
86
87 while (start < end) {
88 // Skip leading spaces.
89 while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
90 start++;
91 startIndex++;
92 }
93
94 // Look for the end of the string, or another space.
95 while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
96 start++;
97 count++;
98 }
99
100 // Grab the value found and process it.
101 if (count > 0) {
102 if (!styleArray) styleArray = new nsTArray<int8_t>();
103
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;
109 }
110
111 nsDependentSubstring valueString(aString, startIndex, count);
112 int8_t styleValue = ParseStyleValue(aAttribute, valueString);
113 styleArray->AppendElement(styleValue);
114
115 startIndex += count;
116 count = 0;
117 }
118 }
119 return styleArray;
120 }
121
ReportParseError(nsIFrame * aFrame,const char16_t * aAttribute,const char16_t * aValue)122 static nsresult ReportParseError(nsIFrame* aFrame, const char16_t* aAttribute,
123 const char16_t* aValue) {
124 nsIContent* content = aFrame->GetContent();
125
126 AutoTArray<nsString, 3> params;
127 params.AppendElement(aValue);
128 params.AppendElement(aAttribute);
129 params.AppendElement(nsDependentAtomString(content->NodeInfo()->NameAtom()));
130
131 return nsContentUtils::ReportToConsole(
132 nsIScriptError::errorFlag, "Layout: MathML"_ns, content->OwnerDoc(),
133 nsContentUtils::eMATHML_PROPERTIES, "AttributeParsingError", params);
134 }
135
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.
140
NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowAlignProperty,nsTArray<int8_t>)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>)
145
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();
153 }
154
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.
160 */
FindCellProperty(const nsIFrame * aCellFrame,const FramePropertyDescriptor<nsTArray<int8_t>> * aFrameProperty)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;
166
167 while (currentFrame) {
168 propertyData = currentFrame->GetProperty(aFrameProperty);
169 bool frameIsTable = (currentFrame->IsTableFrame());
170
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
175 }
176
177 return propertyData;
178 }
179
ApplyBorderToStyle(const nsMathMLmtdFrame * aFrame,nsStyleBorder & aStyleBorder)180 static void ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
181 nsStyleBorder& aStyleBorder) {
182 uint32_t rowIndex = aFrame->RowIndex();
183 uint32_t columnIndex = aFrame->ColIndex();
184
185 nscoord borderWidth = nsPresContext::CSSPixelsToAppUnits(1);
186
187 nsTArray<int8_t>* rowLinesList = FindCellProperty(aFrame, RowLinesProperty());
188
189 nsTArray<int8_t>* columnLinesList =
190 FindCellProperty(aFrame, ColumnLinesProperty());
191
192 // We don't place a row line on top of the first row
193 if (rowIndex > 0 && rowLinesList) {
194 // If the row number is greater than the number of provided rowline
195 // values, we simply repeat the last value.
196 uint32_t listLength = rowLinesList->Length();
197 if (rowIndex < listLength) {
198 aStyleBorder.SetBorderStyle(
199 eSideTop,
200 static_cast<StyleBorderStyle>(rowLinesList->ElementAt(rowIndex - 1)));
201 } else {
202 aStyleBorder.SetBorderStyle(eSideTop,
203 static_cast<StyleBorderStyle>(
204 rowLinesList->ElementAt(listLength - 1)));
205 }
206 aStyleBorder.SetBorderWidth(eSideTop, borderWidth);
207 }
208
209 // We don't place a column line on the left of the first column.
210 if (columnIndex > 0 && columnLinesList) {
211 // If the column number is greater than the number of provided columline
212 // values, we simply repeat the last value.
213 uint32_t listLength = columnLinesList->Length();
214 if (columnIndex < listLength) {
215 aStyleBorder.SetBorderStyle(
216 eSideLeft, static_cast<StyleBorderStyle>(
217 columnLinesList->ElementAt(columnIndex - 1)));
218 } else {
219 aStyleBorder.SetBorderStyle(
220 eSideLeft, static_cast<StyleBorderStyle>(
221 columnLinesList->ElementAt(listLength - 1)));
222 }
223 aStyleBorder.SetBorderWidth(eSideLeft, borderWidth);
224 }
225 }
226
ComputeBorderOverflow(nsMathMLmtdFrame * aFrame,const nsStyleBorder & aStyleBorder)227 static nsMargin ComputeBorderOverflow(nsMathMLmtdFrame* aFrame,
228 const nsStyleBorder& aStyleBorder) {
229 nsMargin overflow;
230 int32_t rowIndex;
231 int32_t columnIndex;
232 nsTableFrame* table = aFrame->GetTableFrame();
233 aFrame->GetCellIndexes(rowIndex, columnIndex);
234 if (!columnIndex) {
235 overflow.left = table->GetColSpacing(-1);
236 overflow.right = table->GetColSpacing(0) / 2;
237 } else if (columnIndex == table->GetColCount() - 1) {
238 overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
239 overflow.right = table->GetColSpacing(columnIndex + 1);
240 } else {
241 overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
242 overflow.right = table->GetColSpacing(columnIndex) / 2;
243 }
244 if (!rowIndex) {
245 overflow.top = table->GetRowSpacing(-1);
246 overflow.bottom = table->GetRowSpacing(0) / 2;
247 } else if (rowIndex == table->GetRowCount() - 1) {
248 overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
249 overflow.bottom = table->GetRowSpacing(rowIndex + 1);
250 } else {
251 overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
252 overflow.bottom = table->GetRowSpacing(rowIndex) / 2;
253 }
254 return overflow;
255 }
256
257 /*
258 * A variant of the nsDisplayBorder contains special code to render a border
259 * around a nsMathMLmtdFrame based on the rowline and columnline properties
260 * set on the cell frame.
261 */
262 class nsDisplaymtdBorder final : public nsDisplayBorder {
263 public:
nsDisplaymtdBorder(nsDisplayListBuilder * aBuilder,nsMathMLmtdFrame * aFrame)264 nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
265 : nsDisplayBorder(aBuilder, aFrame) {}
266
AllocateGeometry(nsDisplayListBuilder * aBuilder)267 nsDisplayItemGeometry* AllocateGeometry(
268 nsDisplayListBuilder* aBuilder) override {
269 return new nsDisplayItemGenericImageGeometry(this, aBuilder);
270 }
271
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const272 void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
273 const nsDisplayItemGeometry* aGeometry,
274 nsRegion* aInvalidRegion) const override {
275 auto geometry =
276 static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
277
278 if (aBuilder->ShouldSyncDecodeImages() &&
279 geometry->ShouldInvalidateToSyncDecodeImages()) {
280 bool snap;
281 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
282 }
283
284 nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry,
285 aInvalidRegion);
286 }
287
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const288 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
289 bool* aSnap) const override {
290 *aSnap = true;
291 nsStyleBorder styleBorder = *mFrame->StyleBorder();
292 nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
293 ApplyBorderToStyle(frame, styleBorder);
294 nsRect bounds = CalculateBounds<nsRect>(styleBorder);
295 nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
296 bounds.Inflate(overflow);
297 return bounds;
298 }
299
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)300 virtual void Paint(nsDisplayListBuilder* aBuilder,
301 gfxContext* aCtx) override {
302 nsStyleBorder styleBorder = *mFrame->StyleBorder();
303 nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
304 ApplyBorderToStyle(frame, styleBorder);
305
306 nsRect bounds = nsRect(ToReferenceFrame(), mFrame->GetSize());
307 nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
308 bounds.Inflate(overflow);
309
310 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
311 ? PaintBorderFlags::SyncDecodeImages
312 : PaintBorderFlags();
313
314 ImgDrawResult result = nsCSSRendering::PaintBorderWithStyleBorder(
315 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(), bounds,
316 styleBorder, mFrame->Style(), flags, mFrame->GetSkipSides());
317
318 nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
319 }
320
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)321 bool CreateWebRenderCommands(
322 mozilla::wr::DisplayListBuilder& aBuilder,
323 mozilla::wr::IpcResourceUpdateQueue& aResources,
324 const StackingContextHelper& aSc,
325 mozilla::layers::RenderRootStateManager* aManager,
326 nsDisplayListBuilder* aDisplayListBuilder) override {
327 return false;
328 }
329
IsInvisibleInRect(const nsRect & aRect) const330 virtual bool IsInvisibleInRect(const nsRect& aRect) const override {
331 return false;
332 }
333 };
334
335 #ifdef DEBUG
336 # define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
337 MOZ_ASSERT( \
338 mozilla::StyleDisplay::_expected == _frame->StyleDisplay()->mDisplay, \
339 "internal error");
340 #else
341 # define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
342 #endif
343
ParseFrameAttribute(nsIFrame * aFrame,nsAtom * aAttribute,bool aAllowMultiValues)344 static void ParseFrameAttribute(nsIFrame* aFrame, nsAtom* aAttribute,
345 bool aAllowMultiValues) {
346 nsAutoString attrValue;
347
348 Element* frameElement = aFrame->GetContent()->AsElement();
349 frameElement->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
350
351 if (!attrValue.IsEmpty()) {
352 nsTArray<int8_t>* valueList =
353 ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
354
355 // If valueList is null, that indicates a problem with the attribute value.
356 // Only set properties on a valid attribute value.
357 if (valueList) {
358 // The code reading the property assumes that this list is nonempty.
359 NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
360 aFrame->SetProperty(AttributeToProperty(aAttribute), valueList);
361 } else {
362 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
363 }
364 }
365 }
366
367 // rowspacing
368 //
369 // Specifies the distance between successive rows in an mtable. Multiple
370 // lengths can be specified, each corresponding to its respective position
371 // between rows. For example:
372 //
373 // [ROW_0]
374 // rowspace_0
375 // [ROW_1]
376 // rowspace_1
377 // [ROW_2]
378 //
379 // If the number of row gaps exceeds the number of lengths specified, the final
380 // specified length is repeated. Additional lengths are ignored.
381 //
382 // values: (length)+
383 // default: 1.0ex
384 //
385 // Unitless values are permitted and provide a multiple of the default value
386 // Negative values are forbidden.
387 //
388
389 // columnspacing
390 //
391 // Specifies the distance between successive columns in an mtable. Multiple
392 // lengths can be specified, each corresponding to its respective position
393 // between columns. For example:
394 //
395 // [COLUMN_0] columnspace_0 [COLUMN_1] columnspace_1 [COLUMN_2]
396 //
397 // If the number of column gaps exceeds the number of lengths specified, the
398 // final specified length is repeated. Additional lengths are ignored.
399 //
400 // values: (length)+
401 // default: 0.8em
402 //
403 // Unitless values are permitted and provide a multiple of the default value
404 // Negative values are forbidden.
405 //
406
407 // framespacing
408 //
409 // Specifies the distance between the mtable and its frame (if any). The
410 // first value specified provides the spacing between the left and right edge
411 // of the table and the frame, the second value determines the spacing between
412 // the top and bottom edges and the frame.
413 //
414 // An error is reported if only one length is passed. Any additional lengths
415 // are ignored
416 //
417 // values: length length
418 // default: 0em 0ex If frame attribute is "none" or not specified,
419 // 0.4em 0.5ex otherwise
420 //
421 // Unitless values are permitted and provide a multiple of the default value
422 // Negative values are forbidden.
423 //
424
425 static const float kDefaultRowspacingEx = 1.0f;
426 static const float kDefaultColumnspacingEm = 0.8f;
427 static const float kDefaultFramespacingArg0Em = 0.4f;
428 static const float kDefaultFramespacingArg1Ex = 0.5f;
429
ExtractSpacingValues(const nsAString & aString,nsAtom * aAttribute,nsTArray<nscoord> & aSpacingArray,nsIFrame * aFrame,nscoord aDefaultValue0,nscoord aDefaultValue1,float aFontSizeInflation)430 static void ExtractSpacingValues(const nsAString& aString, nsAtom* aAttribute,
431 nsTArray<nscoord>& aSpacingArray,
432 nsIFrame* aFrame, nscoord aDefaultValue0,
433 nscoord aDefaultValue1,
434 float aFontSizeInflation) {
435 nsPresContext* presContext = aFrame->PresContext();
436 ComputedStyle* computedStyle = aFrame->Style();
437
438 const char16_t* start = aString.BeginReading();
439 const char16_t* end = aString.EndReading();
440
441 int32_t startIndex = 0;
442 int32_t count = 0;
443 int32_t elementNum = 0;
444
445 while (start < end) {
446 // Skip leading spaces.
447 while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
448 start++;
449 startIndex++;
450 }
451
452 // Look for the end of the string, or another space.
453 while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
454 start++;
455 count++;
456 }
457
458 // Grab the value found and process it.
459 if (count > 0) {
460 const nsAString& str = Substring(aString, startIndex, count);
461 nsAutoString valueString;
462 valueString.Assign(str);
463 nscoord newValue;
464 if (aAttribute == nsGkAtoms::framespacing_ && elementNum) {
465 newValue = aDefaultValue1;
466 } else {
467 newValue = aDefaultValue0;
468 }
469 nsMathMLFrame::ParseNumericValue(
470 valueString, &newValue, dom::MathMLElement::PARSE_ALLOW_UNITLESS,
471 presContext, computedStyle, aFontSizeInflation);
472 aSpacingArray.AppendElement(newValue);
473
474 startIndex += count;
475 count = 0;
476 elementNum++;
477 }
478 }
479 }
480
ParseSpacingAttribute(nsMathMLmtableFrame * aFrame,nsAtom * aAttribute)481 static void ParseSpacingAttribute(nsMathMLmtableFrame* aFrame,
482 nsAtom* aAttribute) {
483 NS_ASSERTION(aAttribute == nsGkAtoms::rowspacing_ ||
484 aAttribute == nsGkAtoms::columnspacing_ ||
485 aAttribute == nsGkAtoms::framespacing_,
486 "Non spacing attribute passed");
487
488 nsAutoString attrValue;
489 Element* frameElement = aFrame->GetContent()->AsElement();
490 frameElement->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
491
492 if (nsGkAtoms::framespacing_ == aAttribute) {
493 nsAutoString frame;
494 frameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::frame, frame);
495 if (frame.IsEmpty() || frame.EqualsLiteral("none")) {
496 aFrame->SetFrameSpacing(0, 0);
497 return;
498 }
499 }
500
501 nscoord value;
502 nscoord value2;
503 // Set defaults
504 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
505 RefPtr<nsFontMetrics> fm =
506 nsLayoutUtils::GetFontMetricsForFrame(aFrame, fontSizeInflation);
507 if (nsGkAtoms::rowspacing_ == aAttribute) {
508 value = kDefaultRowspacingEx * fm->XHeight();
509 value2 = 0;
510 } else if (nsGkAtoms::columnspacing_ == aAttribute) {
511 value = kDefaultColumnspacingEm * fm->EmHeight();
512 value2 = 0;
513 } else {
514 value = kDefaultFramespacingArg0Em * fm->EmHeight();
515 value2 = kDefaultFramespacingArg1Ex * fm->XHeight();
516 }
517
518 nsTArray<nscoord> valueList;
519 ExtractSpacingValues(attrValue, aAttribute, valueList, aFrame, value, value2,
520 fontSizeInflation);
521 if (valueList.Length() == 0) {
522 if (frameElement->HasAttr(kNameSpaceID_None, aAttribute)) {
523 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
524 }
525 valueList.AppendElement(value);
526 }
527 if (aAttribute == nsGkAtoms::framespacing_) {
528 if (valueList.Length() == 1) {
529 if (frameElement->HasAttr(kNameSpaceID_None, aAttribute)) {
530 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
531 }
532 valueList.AppendElement(value2);
533 } else if (valueList.Length() != 2) {
534 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
535 }
536 }
537
538 if (aAttribute == nsGkAtoms::rowspacing_) {
539 aFrame->SetRowSpacingArray(valueList);
540 } else if (aAttribute == nsGkAtoms::columnspacing_) {
541 aFrame->SetColSpacingArray(valueList);
542 } else {
543 aFrame->SetFrameSpacing(valueList.ElementAt(0), valueList.ElementAt(1));
544 }
545 }
546
ParseSpacingAttributes(nsMathMLmtableFrame * aTableFrame)547 static void ParseSpacingAttributes(nsMathMLmtableFrame* aTableFrame) {
548 ParseSpacingAttribute(aTableFrame, nsGkAtoms::rowspacing_);
549 ParseSpacingAttribute(aTableFrame, nsGkAtoms::columnspacing_);
550 ParseSpacingAttribute(aTableFrame, nsGkAtoms::framespacing_);
551 aTableFrame->SetUseCSSSpacing();
552 }
553
554 // map all attributes within a table -- requires the indices of rows and cells.
555 // so it can only happen after they are made ready by the table base class.
MapAllAttributesIntoCSS(nsMathMLmtableFrame * aTableFrame)556 static void MapAllAttributesIntoCSS(nsMathMLmtableFrame* aTableFrame) {
557 // Map mtable rowalign & rowlines.
558 ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
559 ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
560
561 // Map mtable columnalign & columnlines.
562 ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true);
563 ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true);
564
565 // Map mtable rowspacing, columnspacing & framespacing
566 ParseSpacingAttributes(aTableFrame);
567
568 // mtable is simple and only has one (pseudo) row-group
569 nsIFrame* rgFrame = aTableFrame->PrincipalChildList().FirstChild();
570 if (!rgFrame || !rgFrame->IsTableRowGroupFrame()) return;
571
572 for (nsIFrame* rowFrame : rgFrame->PrincipalChildList()) {
573 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TableRow);
574 if (rowFrame->IsTableRowFrame()) {
575 // Map row rowalign.
576 ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false);
577 // Map row columnalign.
578 ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true);
579
580 for (nsIFrame* cellFrame : rowFrame->PrincipalChildList()) {
581 DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TableCell);
582 if (cellFrame->IsTableCellFrame()) {
583 // Map cell rowalign.
584 ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false);
585 // Map row columnalign.
586 ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false);
587 }
588 }
589 }
590 }
591 }
592
593 // the align attribute of mtable can have a row number which indicates
594 // from where to anchor the table, e.g., top 5 means anchor the table at
595 // the top of the 5th row, axis -1 means anchor the table on the axis of
596 // the last row
597
598 // The REC says that the syntax is
599 // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*'
600 // the parsing could have been simpler with that syntax
601 // but for backward compatibility we make optional
602 // the whitespaces between the alignment name and the row number
603
604 enum eAlign {
605 eAlign_top,
606 eAlign_bottom,
607 eAlign_center,
608 eAlign_baseline,
609 eAlign_axis
610 };
611
ParseAlignAttribute(nsString & aValue,eAlign & aAlign,int32_t & aRowIndex)612 static void ParseAlignAttribute(nsString& aValue, eAlign& aAlign,
613 int32_t& aRowIndex) {
614 // by default, the table is centered about the axis
615 aRowIndex = 0;
616 aAlign = eAlign_axis;
617 int32_t len = 0;
618
619 // we only have to remove the leading spaces because
620 // ToInteger ignores the whitespaces around the number
621 aValue.CompressWhitespace(true, false);
622
623 if (0 == aValue.Find("top")) {
624 len = 3; // 3 is the length of 'top'
625 aAlign = eAlign_top;
626 } else if (0 == aValue.Find("bottom")) {
627 len = 6; // 6 is the length of 'bottom'
628 aAlign = eAlign_bottom;
629 } else if (0 == aValue.Find("center")) {
630 len = 6; // 6 is the length of 'center'
631 aAlign = eAlign_center;
632 } else if (0 == aValue.Find("baseline")) {
633 len = 8; // 8 is the length of 'baseline'
634 aAlign = eAlign_baseline;
635 } else if (0 == aValue.Find("axis")) {
636 len = 4; // 4 is the length of 'axis'
637 aAlign = eAlign_axis;
638 }
639 if (len) {
640 nsresult error;
641 aValue.Cut(0, len); // aValue is not a const here
642 aRowIndex = aValue.ToInteger(&error);
643 if (NS_FAILED(error)) aRowIndex = 0;
644 }
645 }
646
647 #ifdef DEBUG_rbs_off
648 // call ListMathMLTree(mParent) to get the big picture
ListMathMLTree(nsIFrame * atLeast)649 static void ListMathMLTree(nsIFrame* atLeast) {
650 // climb up to <math> or <body> if <math> isn't there
651 nsIFrame* f = atLeast;
652 for (; f; f = f->GetParent()) {
653 nsIContent* c = f->GetContent();
654 if (!c || c->IsMathMLElement(nsGkAtoms::math) ||
655 // XXXbaku which kind of body tag?
656 c->NodeInfo()->NameAtom(nsGkAtoms::body))
657 break;
658 }
659 if (!f) f = atLeast;
660 f->List(stdout, 0);
661 }
662 #endif
663
664 // --------
665 // implementation of nsMathMLmtableWrapperFrame
666
667 NS_QUERYFRAME_HEAD(nsMathMLmtableWrapperFrame)
NS_QUERYFRAME_ENTRY(nsIMathMLFrame)668 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
669 NS_QUERYFRAME_TAIL_INHERITING(nsTableWrapperFrame)
670
671 nsContainerFrame* NS_NewMathMLmtableOuterFrame(PresShell* aPresShell,
672 ComputedStyle* aStyle) {
673 return new (aPresShell)
674 nsMathMLmtableWrapperFrame(aStyle, aPresShell->GetPresContext());
675 }
676
677 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableWrapperFrame)
678
679 nsMathMLmtableWrapperFrame::~nsMathMLmtableWrapperFrame() = default;
680
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)681 nsresult nsMathMLmtableWrapperFrame::AttributeChanged(int32_t aNameSpaceID,
682 nsAtom* aAttribute,
683 int32_t aModType) {
684 // Attributes specific to <mtable>:
685 // frame : in mathml.css
686 // framespacing : here
687 // groupalign : not yet supported
688 // equalrows : not yet supported
689 // equalcolumns : not yet supported
690 // displaystyle : here and in mathml.css
691 // align : in reflow
692 // rowalign : here
693 // rowlines : here
694 // rowspacing : here
695 // columnalign : here
696 // columnlines : here
697 // columnspacing : here
698
699 // mtable is simple and only has one (pseudo) row-group inside our inner-table
700 nsIFrame* tableFrame = mFrames.FirstChild();
701 NS_ASSERTION(tableFrame && tableFrame->IsTableFrame(),
702 "should always have an inner table frame");
703 nsIFrame* rgFrame = tableFrame->PrincipalChildList().FirstChild();
704 if (!rgFrame || !rgFrame->IsTableRowGroupFrame()) return NS_OK;
705
706 // align - just need to issue a dirty (resize) reflow command
707 if (aAttribute == nsGkAtoms::align) {
708 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::Resize,
709 NS_FRAME_IS_DIRTY);
710 return NS_OK;
711 }
712
713 // displaystyle - may seem innocuous, but it is actually very harsh --
714 // like changing an unit. Blow away and recompute all our automatic
715 // presentational data, and issue a style-changed reflow request
716 if (aAttribute == nsGkAtoms::displaystyle_) {
717 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(GetParent());
718 // Need to reflow the parent, not us, because this can actually
719 // affect siblings.
720 PresShell()->FrameNeedsReflow(GetParent(), IntrinsicDirty::StyleChange,
721 NS_FRAME_IS_DIRTY);
722 return NS_OK;
723 }
724
725 // ...and the other attributes affect rows or columns in one way or another
726
727 nsPresContext* presContext = tableFrame->PresContext();
728 if (aAttribute == nsGkAtoms::rowspacing_ ||
729 aAttribute == nsGkAtoms::columnspacing_ ||
730 aAttribute == nsGkAtoms::framespacing_) {
731 nsMathMLmtableFrame* mathMLmtableFrame = do_QueryFrame(tableFrame);
732 if (mathMLmtableFrame) {
733 ParseSpacingAttribute(mathMLmtableFrame, aAttribute);
734 mathMLmtableFrame->SetUseCSSSpacing();
735 }
736 } else if (aAttribute == nsGkAtoms::rowalign_ ||
737 aAttribute == nsGkAtoms::rowlines_ ||
738 aAttribute == nsGkAtoms::columnalign_ ||
739 aAttribute == nsGkAtoms::columnlines_) {
740 // clear any cached property list for this table
741 tableFrame->RemoveProperty(AttributeToProperty(aAttribute));
742 // Reparse the new attribute on the table.
743 ParseFrameAttribute(tableFrame, aAttribute, true);
744 } else {
745 // Ignore attributes that do not affect layout.
746 return NS_OK;
747 }
748
749 // Explicitly request a reflow in our subtree to pick up any changes
750 presContext->PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
751 NS_FRAME_IS_DIRTY);
752
753 return NS_OK;
754 }
755
GetRowFrameAt(int32_t aRowIndex)756 nsIFrame* nsMathMLmtableWrapperFrame::GetRowFrameAt(int32_t aRowIndex) {
757 int32_t rowCount = GetRowCount();
758
759 // Negative indices mean to find upwards from the end.
760 if (aRowIndex < 0) {
761 aRowIndex = rowCount + aRowIndex;
762 } else {
763 // aRowIndex is 1-based, so convert it to a 0-based index
764 --aRowIndex;
765 }
766
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->IsTableFrame(),
771 "should always have an inner table frame");
772 nsIFrame* rgFrame = tableFrame->PrincipalChildList().FirstChild();
773 if (!rgFrame || !rgFrame->IsTableRowGroupFrame()) return nullptr;
774 for (nsIFrame* rowFrame : rgFrame->PrincipalChildList()) {
775 if (aRowIndex == 0) {
776 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TableRow);
777 if (!rowFrame->IsTableRowFrame()) return nullptr;
778
779 return rowFrame;
780 }
781 --aRowIndex;
782 }
783 }
784 return nullptr;
785 }
786
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)787 void nsMathMLmtableWrapperFrame::Reflow(nsPresContext* aPresContext,
788 ReflowOutput& aDesiredSize,
789 const ReflowInput& aReflowInput,
790 nsReflowStatus& aStatus) {
791 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
792
793 nsAutoString value;
794 // we want to return a table that is anchored according to the align attribute
795
796 nsTableWrapperFrame::Reflow(aPresContext, aDesiredSize, aReflowInput,
797 aStatus);
798 NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
799 NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");
800
801 // see if the user has set the align attribute on the <mtable>
802 int32_t rowIndex = 0;
803 eAlign tableAlign = eAlign_axis;
804 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
805 if (!value.IsEmpty()) {
806 ParseAlignAttribute(value, tableAlign, rowIndex);
807 }
808
809 // adjustments if there is a specified row from where to anchor the table
810 // (conceptually: when there is no row of reference, picture the table as if
811 // it is wrapped in a single big fictional row at dy = 0, this way of
812 // doing so allows us to have a single code path for all cases).
813 nscoord dy = 0;
814 WritingMode wm = aDesiredSize.GetWritingMode();
815 nscoord blockSize = aDesiredSize.BSize(wm);
816 nsIFrame* rowFrame = nullptr;
817 if (rowIndex) {
818 rowFrame = GetRowFrameAt(rowIndex);
819 if (rowFrame) {
820 // translate the coordinates to be relative to us and in our writing mode
821 nsIFrame* frame = rowFrame;
822 LogicalRect rect(wm, frame->GetRect(),
823 aReflowInput.ComputedSizeAsContainerIfConstrained());
824 blockSize = rect.BSize(wm);
825 do {
826 nsIFrame* parent = frame->GetParent();
827 dy += frame->BStart(wm, parent->GetSize());
828 frame = parent;
829 } while (frame != this);
830 }
831 }
832 switch (tableAlign) {
833 case eAlign_top:
834 aDesiredSize.SetBlockStartAscent(dy);
835 break;
836 case eAlign_bottom:
837 aDesiredSize.SetBlockStartAscent(dy + blockSize);
838 break;
839 case eAlign_center:
840 aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
841 break;
842 case eAlign_baseline:
843 if (rowFrame) {
844 // anchor the table on the baseline of the row of reference
845 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
846 if (rowAscent) { // the row has at least one cell with 'vertical-align:
847 // baseline'
848 aDesiredSize.SetBlockStartAscent(dy + rowAscent);
849 break;
850 }
851 }
852 // in other situations, fallback to center
853 aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
854 break;
855 case eAlign_axis:
856 default: {
857 // XXX should instead use style data from the row of reference here ?
858 RefPtr<nsFontMetrics> fm =
859 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
860 nscoord axisHeight;
861 GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm,
862 axisHeight);
863 if (rowFrame) {
864 // anchor the table on the axis of the row of reference
865 // XXX fallback to baseline because it is a hard problem
866 // XXX need to fetch the axis of the row; would need rowalign=axis to
867 // work better
868 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
869 if (rowAscent) { // the row has at least one cell with 'vertical-align:
870 // baseline'
871 aDesiredSize.SetBlockStartAscent(dy + rowAscent);
872 break;
873 }
874 }
875 // in other situations, fallback to using half of the height
876 aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight);
877 }
878 }
879
880 mReference.x = 0;
881 mReference.y = aDesiredSize.BlockStartAscent();
882
883 // just make-up a bounding metrics
884 mBoundingMetrics = nsBoundingMetrics();
885 mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
886 mBoundingMetrics.descent =
887 aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
888 mBoundingMetrics.width = aDesiredSize.Width();
889 mBoundingMetrics.leftBearing = 0;
890 mBoundingMetrics.rightBearing = aDesiredSize.Width();
891
892 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
893 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
894 }
895
NS_NewMathMLmtableFrame(PresShell * aPresShell,ComputedStyle * aStyle)896 nsContainerFrame* NS_NewMathMLmtableFrame(PresShell* aPresShell,
897 ComputedStyle* aStyle) {
898 return new (aPresShell)
899 nsMathMLmtableFrame(aStyle, aPresShell->GetPresContext());
900 }
901
902 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
903
904 nsMathMLmtableFrame::~nsMathMLmtableFrame() = default;
905
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)906 void nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID,
907 nsFrameList& aChildList) {
908 nsTableFrame::SetInitialChildList(aListID, aChildList);
909 MapAllAttributesIntoCSS(this);
910 }
911
RestyleTable()912 void nsMathMLmtableFrame::RestyleTable() {
913 // re-sync MathML specific style data that may have changed
914 MapAllAttributesIntoCSS(this);
915
916 // Explicitly request a re-resolve and reflow in our subtree to pick up any
917 // changes
918 PresContext()->RestyleManager()->PostRestyleEvent(
919 mContent->AsElement(), RestyleHint::RestyleSubtree(),
920 nsChangeHint_AllReflowHints);
921 }
922
GetColSpacing(int32_t aColIndex)923 nscoord nsMathMLmtableFrame::GetColSpacing(int32_t aColIndex) {
924 if (mUseCSSSpacing) {
925 return nsTableFrame::GetColSpacing(aColIndex);
926 }
927 if (!mColSpacing.Length()) {
928 NS_ERROR("mColSpacing should not be empty");
929 return 0;
930 }
931 if (aColIndex < 0 || aColIndex >= GetColCount()) {
932 NS_ASSERTION(aColIndex == -1 || aColIndex == GetColCount(),
933 "Desired column beyond bounds of table and border");
934 return mFrameSpacingX;
935 }
936 if ((uint32_t)aColIndex >= mColSpacing.Length()) {
937 return mColSpacing.LastElement();
938 }
939 return mColSpacing.ElementAt(aColIndex);
940 }
941
GetColSpacing(int32_t aStartColIndex,int32_t aEndColIndex)942 nscoord nsMathMLmtableFrame::GetColSpacing(int32_t aStartColIndex,
943 int32_t aEndColIndex) {
944 if (mUseCSSSpacing) {
945 return nsTableFrame::GetColSpacing(aStartColIndex, aEndColIndex);
946 }
947 if (aStartColIndex == aEndColIndex) {
948 return 0;
949 }
950 if (!mColSpacing.Length()) {
951 NS_ERROR("mColSpacing should not be empty");
952 return 0;
953 }
954 nscoord space = 0;
955 if (aStartColIndex < 0) {
956 NS_ASSERTION(aStartColIndex == -1,
957 "Desired column beyond bounds of table and border");
958 space += mFrameSpacingX;
959 aStartColIndex = 0;
960 }
961 if (aEndColIndex >= GetColCount()) {
962 NS_ASSERTION(aEndColIndex == GetColCount(),
963 "Desired column beyond bounds of table and border");
964 space += mFrameSpacingX;
965 aEndColIndex = GetColCount();
966 }
967 // Only iterate over column spacing when there is the potential to vary
968 int32_t min = std::min(aEndColIndex, (int32_t)mColSpacing.Length());
969 for (int32_t i = aStartColIndex; i < min; i++) {
970 space += mColSpacing.ElementAt(i);
971 }
972 // The remaining values are constant. Note that if there are more
973 // column spacings specified than there are columns, LastElement() will be
974 // multiplied by 0, so it is still safe to use.
975 space += (aEndColIndex - min) * mColSpacing.LastElement();
976 return space;
977 }
978
GetRowSpacing(int32_t aRowIndex)979 nscoord nsMathMLmtableFrame::GetRowSpacing(int32_t aRowIndex) {
980 if (mUseCSSSpacing) {
981 return nsTableFrame::GetRowSpacing(aRowIndex);
982 }
983 if (!mRowSpacing.Length()) {
984 NS_ERROR("mRowSpacing should not be empty");
985 return 0;
986 }
987 if (aRowIndex < 0 || aRowIndex >= GetRowCount()) {
988 NS_ASSERTION(aRowIndex == -1 || aRowIndex == GetRowCount(),
989 "Desired row beyond bounds of table and border");
990 return mFrameSpacingY;
991 }
992 if ((uint32_t)aRowIndex >= mRowSpacing.Length()) {
993 return mRowSpacing.LastElement();
994 }
995 return mRowSpacing.ElementAt(aRowIndex);
996 }
997
GetRowSpacing(int32_t aStartRowIndex,int32_t aEndRowIndex)998 nscoord nsMathMLmtableFrame::GetRowSpacing(int32_t aStartRowIndex,
999 int32_t aEndRowIndex) {
1000 if (mUseCSSSpacing) {
1001 return nsTableFrame::GetRowSpacing(aStartRowIndex, aEndRowIndex);
1002 }
1003 if (aStartRowIndex == aEndRowIndex) {
1004 return 0;
1005 }
1006 if (!mRowSpacing.Length()) {
1007 NS_ERROR("mRowSpacing should not be empty");
1008 return 0;
1009 }
1010 nscoord space = 0;
1011 if (aStartRowIndex < 0) {
1012 NS_ASSERTION(aStartRowIndex == -1,
1013 "Desired row beyond bounds of table and border");
1014 space += mFrameSpacingY;
1015 aStartRowIndex = 0;
1016 }
1017 if (aEndRowIndex >= GetRowCount()) {
1018 NS_ASSERTION(aEndRowIndex == GetRowCount(),
1019 "Desired row beyond bounds of table and border");
1020 space += mFrameSpacingY;
1021 aEndRowIndex = GetRowCount();
1022 }
1023 // Only iterate over row spacing when there is the potential to vary
1024 int32_t min = std::min(aEndRowIndex, (int32_t)mRowSpacing.Length());
1025 for (int32_t i = aStartRowIndex; i < min; i++) {
1026 space += mRowSpacing.ElementAt(i);
1027 }
1028 // The remaining values are constant. Note that if there are more
1029 // row spacings specified than there are row, LastElement() will be
1030 // multiplied by 0, so it is still safe to use.
1031 space += (aEndRowIndex - min) * mRowSpacing.LastElement();
1032 return space;
1033 }
1034
SetUseCSSSpacing()1035 void nsMathMLmtableFrame::SetUseCSSSpacing() {
1036 mUseCSSSpacing = !(mContent->AsElement()->HasAttr(kNameSpaceID_None,
1037 nsGkAtoms::rowspacing_) ||
1038 mContent->AsElement()->HasAttr(
1039 kNameSpaceID_None, nsGkAtoms::columnspacing_) ||
1040 mContent->AsElement()->HasAttr(kNameSpaceID_None,
1041 nsGkAtoms::framespacing_));
1042 }
1043
1044 NS_QUERYFRAME_HEAD(nsMathMLmtableFrame)
NS_QUERYFRAME_ENTRY(nsMathMLmtableFrame)1045 NS_QUERYFRAME_ENTRY(nsMathMLmtableFrame)
1046 NS_QUERYFRAME_TAIL_INHERITING(nsTableFrame)
1047
1048 // --------
1049 // implementation of nsMathMLmtrFrame
1050
1051 nsContainerFrame* NS_NewMathMLmtrFrame(PresShell* aPresShell,
1052 ComputedStyle* aStyle) {
1053 return new (aPresShell)
1054 nsMathMLmtrFrame(aStyle, aPresShell->GetPresContext());
1055 }
1056
1057 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
1058
1059 nsMathMLmtrFrame::~nsMathMLmtrFrame() = default;
1060
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)1061 nsresult nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID,
1062 nsAtom* aAttribute,
1063 int32_t aModType) {
1064 // Attributes specific to <mtr>:
1065 // groupalign : Not yet supported.
1066 // rowalign : Here
1067 // columnalign : Here
1068
1069 nsPresContext* presContext = PresContext();
1070
1071 if (aAttribute != nsGkAtoms::rowalign_ &&
1072 aAttribute != nsGkAtoms::columnalign_) {
1073 return NS_OK;
1074 }
1075
1076 RemoveProperty(AttributeToProperty(aAttribute));
1077
1078 bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
1079
1080 // Reparse the new attribute.
1081 ParseFrameAttribute(this, aAttribute, allowMultiValues);
1082
1083 // Explicitly request a reflow in our subtree to pick up any changes
1084 presContext->PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
1085 NS_FRAME_IS_DIRTY);
1086
1087 return NS_OK;
1088 }
1089
1090 // --------
1091 // implementation of nsMathMLmtdFrame
1092
NS_NewMathMLmtdFrame(PresShell * aPresShell,ComputedStyle * aStyle,nsTableFrame * aTableFrame)1093 nsContainerFrame* NS_NewMathMLmtdFrame(PresShell* aPresShell,
1094 ComputedStyle* aStyle,
1095 nsTableFrame* aTableFrame) {
1096 return new (aPresShell) nsMathMLmtdFrame(aStyle, aTableFrame);
1097 }
1098
1099 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
1100
1101 nsMathMLmtdFrame::~nsMathMLmtdFrame() = default;
1102
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)1103 void nsMathMLmtdFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
1104 nsIFrame* aPrevInFlow) {
1105 nsTableCellFrame::Init(aContent, aParent, aPrevInFlow);
1106
1107 // We want to use the ancestor <math> element's font inflation to avoid
1108 // individual cells having their own varying font inflation.
1109 RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
1110 }
1111
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)1112 nsresult nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
1113 nsAtom* aAttribute,
1114 int32_t aModType) {
1115 // Attributes specific to <mtd>:
1116 // groupalign : Not yet supported
1117 // rowalign : here
1118 // columnalign : here
1119 // rowspan : here
1120 // columnspan : here
1121
1122 if (aAttribute == nsGkAtoms::rowalign_ ||
1123 aAttribute == nsGkAtoms::columnalign_) {
1124 RemoveProperty(AttributeToProperty(aAttribute));
1125
1126 // Reparse the attribute.
1127 ParseFrameAttribute(this, aAttribute, false);
1128 return NS_OK;
1129 }
1130
1131 if (aAttribute == nsGkAtoms::rowspan ||
1132 aAttribute == nsGkAtoms::columnspan_) {
1133 // use the naming expected by the base class
1134 if (aAttribute == nsGkAtoms::columnspan_) aAttribute = nsGkAtoms::colspan;
1135 return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute,
1136 aModType);
1137 }
1138
1139 return NS_OK;
1140 }
1141
GetVerticalAlign() const1142 StyleVerticalAlignKeyword nsMathMLmtdFrame::GetVerticalAlign() const {
1143 // Set the default alignment in case no alignment was specified
1144 auto alignment = nsTableCellFrame::GetVerticalAlign();
1145
1146 nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
1147
1148 if (alignmentList) {
1149 uint32_t rowIndex = RowIndex();
1150
1151 // If the row number is greater than the number of provided rowalign values,
1152 // we simply repeat the last value.
1153 return static_cast<StyleVerticalAlignKeyword>(
1154 (rowIndex < alignmentList->Length())
1155 ? alignmentList->ElementAt(rowIndex)
1156 : alignmentList->LastElement());
1157 }
1158
1159 return alignment;
1160 }
1161
ProcessBorders(nsTableFrame * aFrame,nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)1162 nsresult nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
1163 nsDisplayListBuilder* aBuilder,
1164 const nsDisplayListSet& aLists) {
1165 aLists.BorderBackground()->AppendNewToTop<nsDisplaymtdBorder>(aBuilder, this);
1166 return NS_OK;
1167 }
1168
GetBorderWidth(WritingMode aWM) const1169 LogicalMargin nsMathMLmtdFrame::GetBorderWidth(WritingMode aWM) const {
1170 nsStyleBorder styleBorder = *StyleBorder();
1171 ApplyBorderToStyle(this, styleBorder);
1172 return LogicalMargin(aWM, styleBorder.GetComputedBorder());
1173 }
1174
GetBorderOverflow()1175 nsMargin nsMathMLmtdFrame::GetBorderOverflow() {
1176 nsStyleBorder styleBorder = *StyleBorder();
1177 ApplyBorderToStyle(this, styleBorder);
1178 nsMargin overflow = ComputeBorderOverflow(this, styleBorder);
1179 return overflow;
1180 }
1181
1182 // --------
1183 // implementation of nsMathMLmtdInnerFrame
1184
1185 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
NS_QUERYFRAME_ENTRY(nsIMathMLFrame)1186 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
1187 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
1188
1189 nsContainerFrame* NS_NewMathMLmtdInnerFrame(PresShell* aPresShell,
1190 ComputedStyle* aStyle) {
1191 return new (aPresShell)
1192 nsMathMLmtdInnerFrame(aStyle, aPresShell->GetPresContext());
1193 }
1194
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)1195 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
1196
1197 nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(ComputedStyle* aStyle,
1198 nsPresContext* aPresContext)
1199 : nsBlockFrame(aStyle, aPresContext, kClassID)
1200 // Make a copy of the parent nsStyleText for later modification.
1201 ,
1202 mUniqueStyleText(MakeUnique<nsStyleText>(*StyleText())) {}
1203
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)1204 void nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
1205 ReflowOutput& aDesiredSize,
1206 const ReflowInput& aReflowInput,
1207 nsReflowStatus& aStatus) {
1208 // Let the base class do the reflow
1209 nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
1210
1211 // more about <maligngroup/> and <malignmark/> later
1212 // ...
1213 }
1214
StyleTextForLineLayout()1215 const nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout() {
1216 // Set the default alignment in case nothing was specified
1217 auto alignment = uint8_t(StyleText()->mTextAlign);
1218
1219 nsTArray<int8_t>* alignmentList =
1220 FindCellProperty(this, ColumnAlignProperty());
1221
1222 if (alignmentList) {
1223 nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent();
1224 uint32_t columnIndex = cellFrame->ColIndex();
1225
1226 // If the column number is greater than the number of provided columalign
1227 // values, we simply repeat the last value.
1228 if (columnIndex < alignmentList->Length())
1229 alignment = alignmentList->ElementAt(columnIndex);
1230 else
1231 alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
1232 }
1233
1234 mUniqueStyleText->mTextAlign = StyleTextAlign(alignment);
1235 return mUniqueStyleText.get();
1236 }
1237
1238 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)1239 void nsMathMLmtdInnerFrame::DidSetComputedStyle(
1240 ComputedStyle* aOldComputedStyle) {
1241 nsBlockFrame::DidSetComputedStyle(aOldComputedStyle);
1242 mUniqueStyleText = MakeUnique<nsStyleText>(*StyleText());
1243 }
1244