layout/mathml/nsMathMLmtableFrame.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsMathMLmtableFrame.h"
     7 #include "nsPresContext.h"
     8 #include "nsStyleContext.h"
     9 #include "nsStyleConsts.h"
    10 #include "nsNameSpaceManager.h"
    11 #include "nsRenderingContext.h"
    12 #include "nsCSSRendering.h"
    14 #include "nsTArray.h"
    15 #include "nsTableFrame.h"
    16 #include "celldata.h"
    18 #include "RestyleManager.h"
    19 #include <algorithm>
    21 #include "nsIScriptError.h"
    22 #include "nsContentUtils.h"
    24 using namespace mozilla;
    26 //
    27 // <mtable> -- table or matrix - implementation
    28 //
    30 static int8_t
    31 ParseStyleValue(nsIAtom* aAttribute, const nsAString& aAttributeValue)
    32 {
    33   if (aAttribute == nsGkAtoms::rowalign_) {
    34     if (aAttributeValue.EqualsLiteral("top"))
    35       return NS_STYLE_VERTICAL_ALIGN_TOP;
    36     else if (aAttributeValue.EqualsLiteral("bottom"))
    37       return NS_STYLE_VERTICAL_ALIGN_BOTTOM;
    38     else if (aAttributeValue.EqualsLiteral("center"))
    39       return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
    40     else
    41       return NS_STYLE_VERTICAL_ALIGN_BASELINE;
    42   } else if (aAttribute == nsGkAtoms::columnalign_) {
    43     if (aAttributeValue.EqualsLiteral("left"))
    44       return NS_STYLE_TEXT_ALIGN_LEFT;
    45     else if (aAttributeValue.EqualsLiteral("right"))
    46       return NS_STYLE_TEXT_ALIGN_RIGHT;
    47     else
    48       return NS_STYLE_TEXT_ALIGN_CENTER;
    49   } else if (aAttribute == nsGkAtoms::rowlines_ ||
    50              aAttribute == nsGkAtoms::columnlines_) {
    51     if (aAttributeValue.EqualsLiteral("solid"))
    52       return NS_STYLE_BORDER_STYLE_SOLID;
    53     else if (aAttributeValue.EqualsLiteral("dashed"))
    54       return NS_STYLE_BORDER_STYLE_DASHED;
    55     else
    56       return NS_STYLE_BORDER_STYLE_NONE;
    57   } else {
    58     MOZ_CRASH("Unrecognized attribute.");
    59   }
    61   return -1;
    62 }
    64 static nsTArray<int8_t>*
    65 ExtractStyleValues(const nsAString& aString, nsIAtom* aAttribute,
    66                    bool aAllowMultiValues)
    67 {
    68   nsTArray<int8_t>* styleArray = nullptr;
    70   const char16_t* start = aString.BeginReading();
    71   const char16_t* end = aString.EndReading();
    73   int32_t startIndex = 0;
    74   int32_t count = 0;
    76   while (start < end) {
    77     // Skip leading spaces.
    78     while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
    79       start++;
    80       startIndex++;
    81     }
    83     // Look for the end of the string, or another space.
    84     while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
    85       start++;
    86       count++;
    87     }
    89     // Grab the value found and process it.
    90     if (count > 0) {
    91       if (!styleArray)
    92         styleArray = new nsTArray<int8_t>();
    94       // We want to return a null array if an attribute gives multiple values,
    95       // but multiple values aren't allowed.
    96       if (styleArray->Length() > 1 && !aAllowMultiValues) {
    97         delete styleArray;
    98         return nullptr;
    99       }
   101       nsDependentSubstring valueString(aString, startIndex, count);
   102       int8_t styleValue = ParseStyleValue(aAttribute, valueString);
   103       styleArray->AppendElement(styleValue);
   105       startIndex += count;
   106       count = 0;
   107     }
   108   }
   109   return styleArray;
   110 }
   112 static nsresult ReportParseError(nsIFrame* aFrame, const char16_t* aAttribute,
   113                                  const char16_t* aValue)
   114 {
   115   nsIContent* content = aFrame->GetContent();
   117   const char16_t* params[] =
   118     { aValue, aAttribute, content->Tag()->GetUTF16String() };
   120   return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
   121                                          NS_LITERAL_CSTRING("MathML"),
   122                                          content->OwnerDoc(),
   123                                          nsContentUtils::eMATHML_PROPERTIES,
   124                                          "AttributeParsingError", params, 3);
   125 }
   127 // Each rowalign='top bottom' or columnalign='left right center' (from
   128 // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
   129 // stored in the property table. Row/Cell frames query the property table
   130 // to see what values apply to them.
   132 static void
   133 DestroyStylePropertyList(void* aPropertyValue)
   134 {
   135   delete static_cast<nsTArray<int8_t>*>(aPropertyValue);
   136 }
   138 NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyStylePropertyList)
   139 NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyStylePropertyList)
   140 NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyStylePropertyList)
   141 NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyStylePropertyList)
   143 static const FramePropertyDescriptor*
   144 AttributeToProperty(nsIAtom* aAttribute)
   145 {
   146   if (aAttribute == nsGkAtoms::rowalign_)
   147     return RowAlignProperty();
   148   if (aAttribute == nsGkAtoms::rowlines_)
   149     return RowLinesProperty();
   150   if (aAttribute == nsGkAtoms::columnalign_)
   151     return ColumnAlignProperty();
   152   NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
   153   return ColumnLinesProperty();
   154 }
   156 /* This method looks for a property that applies to a cell, but it looks
   157  * recursively because some cell properties can come from the cell, a row,
   158  * a table, etc. This function searches through the heirarchy for a property
   159  * and returns its value. The function stops searching after checking a <mtable>
   160  * frame.
   161  */
   162 static nsTArray<int8_t>*
   163 FindCellProperty(const nsIFrame* aCellFrame,
   164                  const FramePropertyDescriptor* aFrameProperty)
   165 {
   166   const nsIFrame* currentFrame = aCellFrame;
   167   nsTArray<int8_t>* propertyData = nullptr;
   169   while (currentFrame) {
   170     FrameProperties props = currentFrame->Properties();
   171     propertyData = static_cast<nsTArray<int8_t>*>(props.Get(aFrameProperty));
   172     bool frameIsTable = (currentFrame->GetType() == nsGkAtoms::tableFrame);
   174     if (propertyData || frameIsTable)
   175       currentFrame = nullptr; // A null frame pointer exits the loop
   176     else
   177       currentFrame = currentFrame->GetParent(); // Go to the parent frame
   178   }
   180   return propertyData;
   181 }
   183 static void
   184 ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
   185                    nsStyleBorder& aStyleBorder)
   186 {
   187   int32_t rowIndex;
   188   int32_t columnIndex;
   189   aFrame->GetRowIndex(rowIndex);
   190   aFrame->GetColIndex(columnIndex);
   192   nscoord borderWidth =
   193     aFrame->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN];
   195   nsTArray<int8_t>* rowLinesList =
   196     FindCellProperty(aFrame, RowLinesProperty());
   198   nsTArray<int8_t>* columnLinesList =
   199     FindCellProperty(aFrame, ColumnLinesProperty());
   201   // We don't place a row line on top of the first row
   202   if (rowIndex > 0 && rowLinesList) {
   203     // If the row number is greater than the number of provided rowline
   204     // values, we simply repeat the last value.
   205     int32_t listLength = rowLinesList->Length();
   206     if (rowIndex < listLength) {
   207       aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
   208                     rowLinesList->ElementAt(rowIndex - 1));
   209     } else {
   210       aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
   211                     rowLinesList->ElementAt(listLength - 1));
   212     }
   213     aStyleBorder.SetBorderWidth(NS_SIDE_TOP, borderWidth);
   214   }
   216   // We don't place a column line on the left of the first column.
   217   if (columnIndex > 0 && columnLinesList) {
   218     // If the column number is greater than the number of provided columline
   219     // values, we simply repeat the last value.
   220     int32_t listLength = columnLinesList->Length();
   221     if (columnIndex < listLength) {
   222       aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
   223                     columnLinesList->ElementAt(columnIndex - 1));
   224     } else {
   225       aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
   226                     columnLinesList->ElementAt(listLength - 1));
   227     }
   228     aStyleBorder.SetBorderWidth(NS_SIDE_LEFT, borderWidth);
   229   }
   230 }
   232 /*
   233  * A variant of the nsDisplayBorder contains special code to render a border
   234  * around a nsMathMLmtdFrame based on the rowline and columnline properties
   235  * set on the cell frame.
   236  */
   237 class nsDisplaymtdBorder : public nsDisplayBorder {
   238 public:
   239   nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
   240     : nsDisplayBorder(aBuilder, aFrame)
   241   {
   242   }
   244   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
   245   {
   246     nsStyleBorder styleBorder = *mFrame->StyleBorder();
   247     ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
   248     return CalculateBounds(styleBorder);
   249   }
   251   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE
   252   {
   253     nsStyleBorder styleBorder = *mFrame->StyleBorder();
   254     ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
   256     nsPoint offset = ToReferenceFrame();
   257     nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx,
   258                                                mFrame, mVisibleRect,
   259                                                nsRect(offset,
   260                                                       mFrame->GetSize()),
   261                                                styleBorder,
   262                                                mFrame->StyleContext(),
   263                                                mFrame->GetSkipSides());
   264   }
   265 };
   267 #ifdef DEBUG
   268 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
   269   NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->StyleDisplay()->mDisplay, "internal error");
   270 #else
   271 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
   272 #endif
   274 static void
   275 ParseFrameAttribute(nsIFrame* aFrame, nsIAtom* aAttribute,
   276                     bool aAllowMultiValues)
   277 {
   278   nsAutoString attrValue;
   280   nsIContent* frameContent = aFrame->GetContent();
   281   frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
   283   if (!attrValue.IsEmpty()) {
   284     nsTArray<int8_t>* valueList =
   285       ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
   287     // If valueList is null, that indicates a problem with the attribute value.
   288     // Only set properties on a valid attribute value.
   289     if (valueList) {
   290       // The code reading the property assumes that this list is nonempty.
   291       NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
   292       FrameProperties props = aFrame->Properties();
   293       props.Set(AttributeToProperty(aAttribute), valueList);
   294     } else {
   295       ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
   296     }
   297   }
   298 }
   300 // map all attribues within a table -- requires the indices of rows and cells.
   301 // so it can only happen after they are made ready by the table base class.
   302 static void
   303 MapAllAttributesIntoCSS(nsIFrame* aTableFrame)
   304 {
   305   // Map mtable rowalign & rowlines.
   306   ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
   307   ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
   309   // Map mtable columnalign & columnlines.
   310   ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true);
   311   ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true);
   313   // mtable is simple and only has one (pseudo) row-group
   314   nsIFrame* rgFrame = aTableFrame->GetFirstPrincipalChild();
   315   if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
   316     return;
   318   nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild();
   319   for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
   320     DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
   321     if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
   322       // Map row rowalign.
   323       ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false);
   324       // Map row columnalign.
   325       ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true);
   327       nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild();
   328       for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
   329         DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL);
   330         if (IS_TABLE_CELL(cellFrame->GetType())) {
   331           // Map cell rowalign.
   332           ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false);
   333           // Map row columnalign.
   334           ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false);
   335         }
   336       }
   337     }
   338   }
   339 }
   341 // the align attribute of mtable can have a row number which indicates
   342 // from where to anchor the table, e.g., top 5 means anchor the table at
   343 // the top of the 5th row, axis -1 means anchor the table on the axis of
   344 // the last row
   346 // The REC says that the syntax is 
   347 // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*' 
   348 // the parsing could have been simpler with that syntax
   349 // but for backward compatibility we make optional 
   350 // the whitespaces between the alignment name and the row number
   352 enum eAlign {
   353   eAlign_top,
   354   eAlign_bottom,
   355   eAlign_center,
   356   eAlign_baseline,
   357   eAlign_axis
   358 };
   360 static void
   361 ParseAlignAttribute(nsString& aValue, eAlign& aAlign, int32_t& aRowIndex)
   362 {
   363   // by default, the table is centered about the axis
   364   aRowIndex = 0;
   365   aAlign = eAlign_axis;
   366   int32_t len = 0;
   368   // we only have to remove the leading spaces because 
   369   // ToInteger ignores the whitespaces around the number
   370   aValue.CompressWhitespace(true, false);
   372   if (0 == aValue.Find("top")) {
   373     len = 3; // 3 is the length of 'top'
   374     aAlign = eAlign_top;
   375   }
   376   else if (0 == aValue.Find("bottom")) {
   377     len = 6; // 6 is the length of 'bottom'
   378     aAlign = eAlign_bottom;
   379   }
   380   else if (0 == aValue.Find("center")) {
   381     len = 6; // 6 is the length of 'center'
   382     aAlign = eAlign_center;
   383   }
   384   else if (0 == aValue.Find("baseline")) {
   385     len = 8; // 8 is the length of 'baseline'
   386     aAlign = eAlign_baseline;
   387   }
   388   else if (0 == aValue.Find("axis")) {
   389     len = 4; // 4 is the length of 'axis'
   390     aAlign = eAlign_axis;
   391   }
   392   if (len) {
   393     nsresult error;
   394     aValue.Cut(0, len); // aValue is not a const here
   395     aRowIndex = aValue.ToInteger(&error);
   396     if (NS_FAILED(error))
   397       aRowIndex = 0;
   398   }
   399 }
   401 #ifdef DEBUG_rbs_off
   402 // call ListMathMLTree(mParent) to get the big picture
   403 static void
   404 ListMathMLTree(nsIFrame* atLeast)
   405 {
   406   // climb up to <math> or <body> if <math> isn't there
   407   nsIFrame* f = atLeast;
   408   for ( ; f; f = f->GetParent()) {
   409     nsIContent* c = f->GetContent();
   410     if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body)
   411       break;
   412   }
   413   if (!f) f = atLeast;
   414   f->List(stdout, 0);
   415 }
   416 #endif
   418 // --------
   419 // implementation of nsMathMLmtableOuterFrame
   421 NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame)
   422   NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
   423 NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame)
   425 nsIFrame*
   426 NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
   427 {
   428   return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
   429 }
   431 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
   433 nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
   434 {
   435 }
   437 nsresult
   438 nsMathMLmtableOuterFrame::AttributeChanged(int32_t  aNameSpaceID,
   439                                            nsIAtom* aAttribute,
   440                                            int32_t  aModType)
   441 {
   442   // Attributes specific to <mtable>:
   443   // frame         : in mathml.css
   444   // framespacing  : not yet supported 
   445   // groupalign    : not yet supported
   446   // equalrows     : not yet supported 
   447   // equalcolumns  : not yet supported 
   448   // displaystyle  : here and in mathml.css
   449   // align         : in reflow 
   450   // rowalign      : here
   451   // rowlines      : here 
   452   // rowspacing    : not yet supported 
   453   // columnalign   : here 
   454   // columnlines   : here 
   455   // columnspacing : not yet supported 
   457   // mtable is simple and only has one (pseudo) row-group inside our inner-table
   458   nsIFrame* tableFrame = mFrames.FirstChild();
   459   NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
   460                "should always have an inner table frame");
   461   nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
   462   if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
   463     return NS_OK;
   465   // align - just need to issue a dirty (resize) reflow command
   466   if (aAttribute == nsGkAtoms::align) {
   467     PresContext()->PresShell()->
   468       FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
   469     return NS_OK;
   470   }
   472   // displaystyle - may seem innocuous, but it is actually very harsh --
   473   // like changing an unit. Blow away and recompute all our automatic
   474   // presentational data, and issue a style-changed reflow request
   475   if (aAttribute == nsGkAtoms::displaystyle_) {
   476     nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent);
   477     // Need to reflow the parent, not us, because this can actually
   478     // affect siblings.
   479     PresContext()->PresShell()->
   480       FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   481     return NS_OK;
   482   }
   484   // ...and the other attributes affect rows or columns in one way or another
   486   // Ignore attributes that do not affect layout.
   487   if (aAttribute != nsGkAtoms::rowalign_ &&
   488       aAttribute != nsGkAtoms::rowlines_ &&
   489       aAttribute != nsGkAtoms::columnalign_ &&
   490       aAttribute != nsGkAtoms::columnlines_) {
   491     return NS_OK;
   492   }
   494   nsPresContext* presContext = tableFrame->PresContext();
   496   // clear any cached property list for this table
   497   presContext->PropertyTable()->
   498     Delete(tableFrame, AttributeToProperty(aAttribute));
   500   // Reparse the new attribute on the table.
   501   ParseFrameAttribute(tableFrame, aAttribute, true);
   503   // Explicitly request a reflow in our subtree to pick up any changes
   504   presContext->PresShell()->
   505       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   507   return NS_OK;
   508 }
   510 nsIFrame*
   511 nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext,
   512                                         int32_t         aRowIndex)
   513 {
   514   int32_t rowCount = GetRowCount();
   516   // Negative indices mean to find upwards from the end.
   517   if (aRowIndex < 0) {
   518     aRowIndex = rowCount + aRowIndex;
   519   } else {
   520     // aRowIndex is 1-based, so convert it to a 0-based index
   521     --aRowIndex;
   522   }
   524   // if our inner table says that the index is valid, find the row now
   525   if (0 <= aRowIndex && aRowIndex <= rowCount) {
   526     nsIFrame* tableFrame = mFrames.FirstChild();
   527     NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
   528                  "should always have an inner table frame");
   529     nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
   530     if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
   531       return nullptr;
   532     nsTableIterator rowIter(*rgFrame);
   533     nsIFrame* rowFrame = rowIter.First();
   534     for ( ; rowFrame; rowFrame = rowIter.Next()) {
   535       if (aRowIndex == 0) {
   536         DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
   537         if (rowFrame->GetType() != nsGkAtoms::tableRowFrame)
   538           return nullptr;
   540         return rowFrame;
   541       }
   542       --aRowIndex;
   543     }
   544   }
   545   return nullptr;
   546 }
   548 nsresult
   549 nsMathMLmtableOuterFrame::Reflow(nsPresContext*          aPresContext,
   550                                  nsHTMLReflowMetrics&     aDesiredSize,
   551                                  const nsHTMLReflowState& aReflowState,
   552                                  nsReflowStatus&          aStatus)
   553 {
   554   nsresult rv;
   555   nsAutoString value;
   556   // we want to return a table that is anchored according to the align attribute
   558   rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
   559                                  aStatus);
   560   NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
   561   NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");
   563   // see if the user has set the align attribute on the <mtable>
   564   int32_t rowIndex = 0;
   565   eAlign tableAlign = eAlign_axis;
   566   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
   567   if (!value.IsEmpty()) {
   568     ParseAlignAttribute(value, tableAlign, rowIndex);
   569   }
   571   // adjustments if there is a specified row from where to anchor the table
   572   // (conceptually: when there is no row of reference, picture the table as if
   573   // it is wrapped in a single big fictional row at dy = 0, this way of
   574   // doing so allows us to have a single code path for all cases).
   575   nscoord dy = 0;
   576   nscoord height = aDesiredSize.Height();
   577   nsIFrame* rowFrame = nullptr;
   578   if (rowIndex) {
   579     rowFrame = GetRowFrameAt(aPresContext, rowIndex);
   580     if (rowFrame) {
   581       // translate the coordinates to be relative to us
   582       nsIFrame* frame = rowFrame;
   583       height = frame->GetSize().height;
   584       do {
   585         dy += frame->GetPosition().y;
   586         frame = frame->GetParent();
   587       } while (frame != this);
   588     }
   589   }
   590   switch (tableAlign) {
   591     case eAlign_top:
   592       aDesiredSize.SetTopAscent(dy);
   593       break;
   594     case eAlign_bottom:
   595       aDesiredSize.SetTopAscent(dy + height);
   596       break;
   597     case eAlign_center:
   598       aDesiredSize.SetTopAscent(dy + height / 2);
   599       break;
   600     case eAlign_baseline:
   601       if (rowFrame) {
   602         // anchor the table on the baseline of the row of reference
   603         nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
   604         if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
   605           aDesiredSize.SetTopAscent(dy + rowAscent);
   606           break;
   607         }
   608       }
   609       // in other situations, fallback to center
   610       aDesiredSize.SetTopAscent(dy + height / 2);
   611       break;
   612     case eAlign_axis:
   613     default: {
   614       // XXX should instead use style data from the row of reference here ?
   615       nsRefPtr<nsFontMetrics> fm;
   616       nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   617       aReflowState.rendContext->SetFont(fm);
   618       nscoord axisHeight;
   619       GetAxisHeight(*aReflowState.rendContext,
   620                     aReflowState.rendContext->FontMetrics(),
   621                     axisHeight);
   622       if (rowFrame) {
   623         // anchor the table on the axis of the row of reference
   624         // XXX fallback to baseline because it is a hard problem
   625         // XXX need to fetch the axis of the row; would need rowalign=axis to work better
   626         nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
   627         if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
   628           aDesiredSize.SetTopAscent(dy + rowAscent);
   629           break;
   630         }
   631       }
   632       // in other situations, fallback to using half of the height
   633       aDesiredSize.SetTopAscent(dy + height / 2 + axisHeight);
   634     }
   635   }
   637   mReference.x = 0;
   638   mReference.y = aDesiredSize.TopAscent();
   640   // just make-up a bounding metrics
   641   mBoundingMetrics = nsBoundingMetrics();
   642   mBoundingMetrics.ascent = aDesiredSize.TopAscent();
   643   mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
   644   mBoundingMetrics.width = aDesiredSize.Width();
   645   mBoundingMetrics.leftBearing = 0;
   646   mBoundingMetrics.rightBearing = aDesiredSize.Width();
   648   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   649   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   651   return rv;
   652 }
   654 nsIFrame*
   655 NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   656 {
   657   return new (aPresShell) nsMathMLmtableFrame(aContext);
   658 }
   660 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
   662 nsMathMLmtableFrame::~nsMathMLmtableFrame()
   663 {
   664 }
   666 nsresult
   667 nsMathMLmtableFrame::SetInitialChildList(ChildListID  aListID,
   668                                          nsFrameList& aChildList)
   669 {
   670   nsresult rv = nsTableFrame::SetInitialChildList(aListID, aChildList);
   671   if (NS_FAILED(rv)) return rv;
   672   MapAllAttributesIntoCSS(this);
   673   return rv;
   674 }
   676 void
   677 nsMathMLmtableFrame::RestyleTable()
   678 {
   679   // re-sync MathML specific style data that may have changed
   680   MapAllAttributesIntoCSS(this);
   682   // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
   683   PresContext()->RestyleManager()->
   684     PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
   685                      nsChangeHint_AllReflowHints);
   686 }
   688 // --------
   689 // implementation of nsMathMLmtrFrame
   691 nsIFrame*
   692 NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   693 {
   694   return new (aPresShell) nsMathMLmtrFrame(aContext);
   695 }
   697 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
   699 nsMathMLmtrFrame::~nsMathMLmtrFrame()
   700 {
   701 }
   703 nsresult
   704 nsMathMLmtrFrame::AttributeChanged(int32_t  aNameSpaceID,
   705                                    nsIAtom* aAttribute,
   706                                    int32_t  aModType)
   707 {
   708   // Attributes specific to <mtr>:
   709   // groupalign  : Not yet supported.
   710   // rowalign    : Here
   711   // columnalign : Here
   713   nsPresContext* presContext = PresContext();
   715   if (aAttribute != nsGkAtoms::rowalign_ &&
   716       aAttribute != nsGkAtoms::columnalign_) {
   717     return NS_OK;
   718   }
   720   presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
   722   bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
   724   // Reparse the new attribute.
   725   ParseFrameAttribute(this, aAttribute, allowMultiValues);
   727   // Explicitly request a reflow in our subtree to pick up any changes
   728   presContext->PresShell()->
   729       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   731   return NS_OK;
   732 }
   734 // --------
   735 // implementation of nsMathMLmtdFrame
   737 nsIFrame*
   738 NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   739 {
   740   return new (aPresShell) nsMathMLmtdFrame(aContext);
   741 }
   743 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
   745 nsMathMLmtdFrame::~nsMathMLmtdFrame()
   746 {
   747 }
   749 int32_t
   750 nsMathMLmtdFrame::GetRowSpan()
   751 {
   752   int32_t rowspan = 1;
   754   // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
   755   if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
   756     nsAutoString value;
   757     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
   758     if (!value.IsEmpty()) {
   759       nsresult error;
   760       rowspan = value.ToInteger(&error);
   761       if (NS_FAILED(error) || rowspan < 0)
   762         rowspan = 1;
   763       rowspan = std::min(rowspan, MAX_ROWSPAN);
   764     }
   765   }
   766   return rowspan;
   767 }
   769 int32_t
   770 nsMathMLmtdFrame::GetColSpan()
   771 {
   772   int32_t colspan = 1;
   774   // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
   775   if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
   776     nsAutoString value;
   777     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
   778     if (!value.IsEmpty()) {
   779       nsresult error;
   780       colspan = value.ToInteger(&error);
   781       if (NS_FAILED(error) || colspan < 0 || colspan > MAX_COLSPAN)
   782         colspan = 1;
   783     }
   784   }
   785   return colspan;
   786 }
   788 nsresult
   789 nsMathMLmtdFrame::AttributeChanged(int32_t  aNameSpaceID,
   790                                    nsIAtom* aAttribute,
   791                                    int32_t  aModType)
   792 {
   793   // Attributes specific to <mtd>:
   794   // groupalign  : Not yet supported
   795   // rowalign    : here
   796   // columnalign : here
   797   // rowspan     : here
   798   // columnspan  : here
   800   if (aAttribute == nsGkAtoms::rowalign_ ||
   801       aAttribute == nsGkAtoms::columnalign_) {
   803     nsPresContext* presContext = PresContext();
   804     presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
   806     // Reparse the attribute.
   807     ParseFrameAttribute(this, aAttribute, false);
   808     return NS_OK;
   809   }
   811   if (aAttribute == nsGkAtoms::rowspan ||
   812       aAttribute == nsGkAtoms::columnspan_) {
   813     // use the naming expected by the base class 
   814     if (aAttribute == nsGkAtoms::columnspan_)
   815       aAttribute = nsGkAtoms::colspan;
   816     return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
   817   }
   819   return NS_OK;
   820 }
   822 uint8_t
   823 nsMathMLmtdFrame::GetVerticalAlign() const
   824 {
   825   // Set the default alignment in case no alignment was specified
   826   uint8_t alignment = nsTableCellFrame::GetVerticalAlign();
   828   nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
   830   if (alignmentList) {
   831     int32_t rowIndex;
   832     GetRowIndex(rowIndex);
   834     // If the row number is greater than the number of provided rowalign values,
   835     // we simply repeat the last value.
   836     if (rowIndex < (int32_t)alignmentList->Length())
   837       alignment = alignmentList->ElementAt(rowIndex);
   838     else
   839       alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
   840   }
   842   return alignment;
   843 }
   845 nsresult
   846 nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
   847                                  nsDisplayListBuilder* aBuilder,
   848                                  const nsDisplayListSet& aLists)
   849 {
   850   aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
   851                                             nsDisplaymtdBorder(aBuilder, this));
   852   return NS_OK;
   853 }
   855 nsMargin*
   856 nsMathMLmtdFrame::GetBorderWidth(nsMargin& aBorder) const
   857 {
   858   nsStyleBorder styleBorder = *StyleBorder();
   859   ApplyBorderToStyle(this, styleBorder);
   860   aBorder = styleBorder.GetComputedBorder();
   861   return &aBorder;
   862 }
   864 // --------
   865 // implementation of nsMathMLmtdInnerFrame
   867 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
   868   NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
   869 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
   871 nsIFrame*
   872 NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   873 {
   874   return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
   875 }
   877 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
   879 nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext* aContext)
   880   : nsBlockFrame(aContext)
   881 {
   882   // Make a copy of the parent nsStyleText for later modificaiton.
   883   mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
   884 }
   886 nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
   887 {
   888   mUniqueStyleText->Destroy(PresContext());
   889 }
   891 nsresult
   892 nsMathMLmtdInnerFrame::Reflow(nsPresContext*          aPresContext,
   893                               nsHTMLReflowMetrics&     aDesiredSize,
   894                               const nsHTMLReflowState& aReflowState,
   895                               nsReflowStatus&          aStatus)
   896 {
   897   // Let the base class do the reflow
   898   nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
   900   // more about <maligngroup/> and <malignmark/> later
   901   // ...
   902   return rv;
   903 }
   905 const
   906 nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
   907 {
   908   // Set the default alignment in case nothing was specified
   909   uint8_t alignment = StyleText()->mTextAlign;
   911   nsTArray<int8_t>* alignmentList =
   912     FindCellProperty(this, ColumnAlignProperty());
   914   if (alignmentList) {
   915     nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent();
   916     int32_t columnIndex;
   917     cellFrame->GetColIndex(columnIndex);
   919     // If the column number is greater than the number of provided columalign
   920     // values, we simply repeat the last value.
   921     if (columnIndex < (int32_t)alignmentList->Length())
   922       alignment = alignmentList->ElementAt(columnIndex);
   923     else
   924       alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
   925   }
   927   mUniqueStyleText->mTextAlign = alignment;
   928   return mUniqueStyleText;
   929 }
   931 /* virtual */ void
   932 nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
   933 {
   934   nsBlockFrame::DidSetStyleContext(aOldStyleContext);
   935   mUniqueStyleText->Destroy(PresContext());
   936   mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
   937 }

mercurial