tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }