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