/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsMathMLmtableFrame.h" #include "nsPresContext.h" #include "nsStyleContext.h" #include "nsStyleConsts.h" #include "nsINameSpaceManager.h" #include "nsRenderingContext.h" #include "nsCSSRendering.h" #include "nsTArray.h" #include "nsTableFrame.h" #include "celldata.h" #include "RestyleManager.h" #include #include "nsIScriptError.h" #include "nsContentUtils.h" using namespace mozilla; // // -- table or matrix - implementation // static int8_t ParseStyleValue(nsIAtom* aAttribute, const nsAString& aAttributeValue) { if (aAttribute == nsGkAtoms::rowalign_) { if (aAttributeValue.EqualsLiteral("top")) return NS_STYLE_VERTICAL_ALIGN_TOP; else if (aAttributeValue.EqualsLiteral("bottom")) return NS_STYLE_VERTICAL_ALIGN_BOTTOM; else if (aAttributeValue.EqualsLiteral("center")) return NS_STYLE_VERTICAL_ALIGN_MIDDLE; else return NS_STYLE_VERTICAL_ALIGN_BASELINE; } else if (aAttribute == nsGkAtoms::columnalign_) { if (aAttributeValue.EqualsLiteral("left")) return NS_STYLE_TEXT_ALIGN_LEFT; else if (aAttributeValue.EqualsLiteral("right")) return NS_STYLE_TEXT_ALIGN_RIGHT; else return NS_STYLE_TEXT_ALIGN_CENTER; } else if (aAttribute == nsGkAtoms::rowlines_ || aAttribute == nsGkAtoms::columnlines_) { if (aAttributeValue.EqualsLiteral("solid")) return NS_STYLE_BORDER_STYLE_SOLID; else if (aAttributeValue.EqualsLiteral("dashed")) return NS_STYLE_BORDER_STYLE_DASHED; else return NS_STYLE_BORDER_STYLE_NONE; } else { MOZ_CRASH("Unrecognized attribute."); } return -1; } static nsTArray* ExtractStyleValues(const nsAString& aString, nsIAtom* aAttribute, bool aAllowMultiValues) { nsTArray* styleArray = nullptr; const char16_t* start = aString.BeginReading(); const char16_t* end = aString.EndReading(); int32_t startIndex = 0; int32_t count = 0; while (start < end) { // Skip leading spaces. while ((start < end) && nsCRT::IsAsciiSpace(*start)) { start++; startIndex++; } // Look for the end of the string, or another space. while ((start < end) && !nsCRT::IsAsciiSpace(*start)) { start++; count++; } // Grab the value found and process it. if (count > 0) { if (!styleArray) styleArray = new nsTArray(); // We want to return a null array if an attribute gives multiple values, // but multiple values aren't allowed. if (styleArray->Length() > 1 && !aAllowMultiValues) { delete styleArray; return nullptr; } nsDependentSubstring valueString(aString, startIndex, count); int8_t styleValue = ParseStyleValue(aAttribute, valueString); styleArray->AppendElement(styleValue); startIndex += count; count = 0; } } return styleArray; } static nsresult ReportParseError(nsIFrame* aFrame, const char16_t* aAttribute, const char16_t* aValue) { nsIContent* content = aFrame->GetContent(); const char16_t* params[] = { aValue, aAttribute, content->Tag()->GetUTF16String() }; return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("MathML"), content->OwnerDoc(), nsContentUtils::eMATHML_PROPERTIES, "AttributeParsingError", params, 3); } // Each rowalign='top bottom' or columnalign='left right center' (from // or ) is split once into an nsTArray which is // stored in the property table. Row/Cell frames query the property table // to see what values apply to them. static void DestroyStylePropertyList(void* aPropertyValue) { delete static_cast*>(aPropertyValue); } NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyStylePropertyList) NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyStylePropertyList) NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyStylePropertyList) NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyStylePropertyList) static const FramePropertyDescriptor* AttributeToProperty(nsIAtom* aAttribute) { if (aAttribute == nsGkAtoms::rowalign_) return RowAlignProperty(); if (aAttribute == nsGkAtoms::rowlines_) return RowLinesProperty(); if (aAttribute == nsGkAtoms::columnalign_) return ColumnAlignProperty(); NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute"); return ColumnLinesProperty(); } /* This method looks for a property that applies to a cell, but it looks * recursively because some cell properties can come from the cell, a row, * a table, etc. This function searches through the heirarchy for a property * and returns its value. The function stops searching after checking a * frame. */ static nsTArray* FindCellProperty(const nsIFrame* aCellFrame, const FramePropertyDescriptor* aFrameProperty) { const nsIFrame* currentFrame = aCellFrame; nsTArray* propertyData = nullptr; while (currentFrame) { FrameProperties props = currentFrame->Properties(); propertyData = static_cast*>(props.Get(aFrameProperty)); bool frameIsTable = (currentFrame->GetType() == nsGkAtoms::tableFrame); if (propertyData || frameIsTable) currentFrame = nullptr; // A null frame pointer exits the loop else currentFrame = currentFrame->GetParent(); // Go to the parent frame } return propertyData; } /* * A variant of the nsDisplayBorder contains special code to render a border * around a nsMathMLmtdFrame based on the rowline and columnline properties * set on the cell frame. */ class nsDisplaymtdBorder : public nsDisplayBorder { public: nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame) : nsDisplayBorder(aBuilder, aFrame) { } virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE { int32_t rowIndex; int32_t columnIndex; static_cast(mFrame)-> GetCellIndexes(rowIndex, columnIndex); nsStyleBorder styleBorder = *mFrame->StyleBorder(); nscoord borderWidth = mFrame->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN]; nsTArray* rowLinesList = FindCellProperty(mFrame, RowLinesProperty()); nsTArray* columnLinesList = FindCellProperty(mFrame, ColumnLinesProperty()); // We don't place a row line on top of the first row if (rowIndex > 0 && rowLinesList) { // If the row number is greater than the number of provided rowline // values, we simply repeat the last value. int32_t listLength = rowLinesList->Length(); if (rowIndex < listLength) { styleBorder.SetBorderStyle(NS_SIDE_TOP, rowLinesList->ElementAt(rowIndex - 1)); } else { styleBorder.SetBorderStyle(NS_SIDE_TOP, rowLinesList->ElementAt(listLength - 1)); } styleBorder.SetBorderWidth(NS_SIDE_TOP, borderWidth); } // We don't place a column line on the left of the first column. if (columnIndex > 0 && columnLinesList) { // If the column number is greater than the number of provided columline // values, we simply repeat the last value. int32_t listLength = columnLinesList->Length(); if (columnIndex < listLength) { styleBorder.SetBorderStyle(NS_SIDE_LEFT, columnLinesList->ElementAt(columnIndex - 1)); } else { styleBorder.SetBorderStyle(NS_SIDE_LEFT, columnLinesList->ElementAt(listLength - 1)); } styleBorder.SetBorderWidth(NS_SIDE_LEFT, borderWidth); } nsPoint offset = ToReferenceFrame(); nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx, mFrame, mVisibleRect, nsRect(offset, mFrame->GetSize()), styleBorder, mFrame->StyleContext(), mFrame->GetSkipSides()); } }; #ifdef DEBUG #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \ NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->StyleDisplay()->mDisplay, "internal error"); #else #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) #endif static void ParseFrameAttribute(nsIFrame* aFrame, nsIAtom* aAttribute, bool aAllowMultiValues) { nsAutoString attrValue; nsIContent* frameContent = aFrame->GetContent(); frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue); if (!attrValue.IsEmpty()) { nsTArray* valueList = ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues); // If valueList is null, that indicates a problem with the attribute value. // Only set properties on a valid attribute value. if (valueList) { // The code reading the property assumes that this list is nonempty. NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!"); FrameProperties props = aFrame->Properties(); props.Set(AttributeToProperty(aAttribute), valueList); } else { ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get()); } } } // map all attribues within a table -- requires the indices of rows and cells. // so it can only happen after they are made ready by the table base class. static void MapAllAttributesIntoCSS(nsIFrame* aTableFrame) { // Map mtable rowalign & rowlines. ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true); ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true); // Map mtable columnalign & columnlines. ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true); ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true); // mtable is simple and only has one (pseudo) row-group nsIFrame* rgFrame = aTableFrame->GetFirstPrincipalChild(); if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame) return; nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild(); for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) { DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW); if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) { // Map row rowalign. ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false); // Map row columnalign. ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true); nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild(); for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) { DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL); if (IS_TABLE_CELL(cellFrame->GetType())) { // Map cell rowalign. ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false); // Map row columnalign. ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false); } } } } } // the align attribute of mtable can have a row number which indicates // from where to anchor the table, e.g., top 5 means anchor the table at // the top of the 5th row, axis -1 means anchor the table on the axis of // the last row // The REC says that the syntax is // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*' // the parsing could have been simpler with that syntax // but for backward compatibility we make optional // the whitespaces between the alignment name and the row number enum eAlign { eAlign_top, eAlign_bottom, eAlign_center, eAlign_baseline, eAlign_axis }; static void ParseAlignAttribute(nsString& aValue, eAlign& aAlign, int32_t& aRowIndex) { // by default, the table is centered about the axis aRowIndex = 0; aAlign = eAlign_axis; int32_t len = 0; // we only have to remove the leading spaces because // ToInteger ignores the whitespaces around the number aValue.CompressWhitespace(true, false); if (0 == aValue.Find("top")) { len = 3; // 3 is the length of 'top' aAlign = eAlign_top; } else if (0 == aValue.Find("bottom")) { len = 6; // 6 is the length of 'bottom' aAlign = eAlign_bottom; } else if (0 == aValue.Find("center")) { len = 6; // 6 is the length of 'center' aAlign = eAlign_center; } else if (0 == aValue.Find("baseline")) { len = 8; // 8 is the length of 'baseline' aAlign = eAlign_baseline; } else if (0 == aValue.Find("axis")) { len = 4; // 4 is the length of 'axis' aAlign = eAlign_axis; } if (len) { nsresult error; aValue.Cut(0, len); // aValue is not a const here aRowIndex = aValue.ToInteger(&error); if (NS_FAILED(error)) aRowIndex = 0; } } #ifdef DEBUG_rbs_off // call ListMathMLTree(mParent) to get the big picture static void ListMathMLTree(nsIFrame* atLeast) { // climb up to or if isn't there nsIFrame* f = atLeast; for ( ; f; f = f->GetParent()) { nsIContent* c = f->GetContent(); if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body) break; } if (!f) f = atLeast; f->List(stdout, 0); } #endif // -------- // implementation of nsMathMLmtableOuterFrame NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame) NS_QUERYFRAME_ENTRY(nsIMathMLFrame) NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame) nsIFrame* NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsMathMLmtableOuterFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame) nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame() { } NS_IMETHODIMP nsMathMLmtableOuterFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType) { // Attributes specific to : // frame : in mathml.css // framespacing : not yet supported // groupalign : not yet supported // equalrows : not yet supported // equalcolumns : not yet supported // displaystyle : here and in mathml.css // align : in reflow // rowalign : here // rowlines : here // rowspacing : not yet supported // columnalign : here // columnlines : here // columnspacing : not yet supported // mtable is simple and only has one (pseudo) row-group inside our inner-table nsIFrame* tableFrame = mFrames.FirstChild(); NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame, "should always have an inner table frame"); nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild(); if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame) return NS_OK; // align - just need to issue a dirty (resize) reflow command if (aAttribute == nsGkAtoms::align) { PresContext()->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); return NS_OK; } // displaystyle - may seem innocuous, but it is actually very harsh -- // like changing an unit. Blow away and recompute all our automatic // presentational data, and issue a style-changed reflow request if (aAttribute == nsGkAtoms::displaystyle_) { nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent); // Need to reflow the parent, not us, because this can actually // affect siblings. PresContext()->PresShell()-> FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); return NS_OK; } // ...and the other attributes affect rows or columns in one way or another // Ignore attributes that do not affect layout. if (aAttribute != nsGkAtoms::rowalign_ && aAttribute != nsGkAtoms::rowlines_ && aAttribute != nsGkAtoms::columnalign_ && aAttribute != nsGkAtoms::columnlines_) { return NS_OK; } nsPresContext* presContext = tableFrame->PresContext(); // clear any cached property list for this table presContext->PropertyTable()-> Delete(tableFrame, AttributeToProperty(aAttribute)); // Reparse the new attribute on the table. ParseFrameAttribute(tableFrame, aAttribute, true); // Explicitly request a reflow in our subtree to pick up any changes presContext->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); return NS_OK; } nsIFrame* nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext, int32_t aRowIndex) { int32_t rowCount = GetRowCount(); // Negative indices mean to find upwards from the end. if (aRowIndex < 0) { aRowIndex = rowCount + aRowIndex; } else { // aRowIndex is 1-based, so convert it to a 0-based index --aRowIndex; } // if our inner table says that the index is valid, find the row now if (0 <= aRowIndex && aRowIndex <= rowCount) { nsIFrame* tableFrame = mFrames.FirstChild(); NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame, "should always have an inner table frame"); nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild(); if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame) return nullptr; nsTableIterator rowIter(*rgFrame); nsIFrame* rowFrame = rowIter.First(); for ( ; rowFrame; rowFrame = rowIter.Next()) { if (aRowIndex == 0) { DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW); if (rowFrame->GetType() != nsGkAtoms::tableRowFrame) return nullptr; return rowFrame; } --aRowIndex; } } return nullptr; } NS_IMETHODIMP nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsresult rv; nsAutoString value; // we want to return a table that is anchored according to the align attribute rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable"); NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable"); // see if the user has set the align attribute on the int32_t rowIndex = 0; eAlign tableAlign = eAlign_axis; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value); if (!value.IsEmpty()) { ParseAlignAttribute(value, tableAlign, rowIndex); } // adjustments if there is a specified row from where to anchor the table // (conceptually: when there is no row of reference, picture the table as if // it is wrapped in a single big fictional row at dy = 0, this way of // doing so allows us to have a single code path for all cases). nscoord dy = 0; nscoord height = aDesiredSize.Height(); nsIFrame* rowFrame = nullptr; if (rowIndex) { rowFrame = GetRowFrameAt(aPresContext, rowIndex); if (rowFrame) { // translate the coordinates to be relative to us nsIFrame* frame = rowFrame; height = frame->GetSize().height; do { dy += frame->GetPosition().y; frame = frame->GetParent(); } while (frame != this); } } switch (tableAlign) { case eAlign_top: aDesiredSize.SetTopAscent(dy); break; case eAlign_bottom: aDesiredSize.SetTopAscent(dy + height); break; case eAlign_center: aDesiredSize.SetTopAscent(dy + height / 2); break; case eAlign_baseline: if (rowFrame) { // anchor the table on the baseline of the row of reference nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent(); if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline' aDesiredSize.SetTopAscent(dy + rowAscent); break; } } // in other situations, fallback to center aDesiredSize.SetTopAscent(dy + height / 2); break; case eAlign_axis: default: { // XXX should instead use style data from the row of reference here ? nsRefPtr fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aReflowState.rendContext->SetFont(fm); nscoord axisHeight; GetAxisHeight(*aReflowState.rendContext, aReflowState.rendContext->FontMetrics(), axisHeight); if (rowFrame) { // anchor the table on the axis of the row of reference // XXX fallback to baseline because it is a hard problem // XXX need to fetch the axis of the row; would need rowalign=axis to work better nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent(); if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline' aDesiredSize.SetTopAscent(dy + rowAscent); break; } } // in other situations, fallback to using half of the height aDesiredSize.SetTopAscent(dy + height / 2 + axisHeight); } } mReference.x = 0; mReference.y = aDesiredSize.TopAscent(); // just make-up a bounding metrics mBoundingMetrics = nsBoundingMetrics(); mBoundingMetrics.ascent = aDesiredSize.TopAscent(); mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent(); mBoundingMetrics.width = aDesiredSize.Width(); mBoundingMetrics.leftBearing = 0; mBoundingMetrics.rightBearing = aDesiredSize.Width(); aDesiredSize.mBoundingMetrics = mBoundingMetrics; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return rv; } nsIFrame* NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsMathMLmtableFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame) nsMathMLmtableFrame::~nsMathMLmtableFrame() { } NS_IMETHODIMP nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) { nsresult rv = nsTableFrame::SetInitialChildList(aListID, aChildList); if (NS_FAILED(rv)) return rv; MapAllAttributesIntoCSS(this); return rv; } void nsMathMLmtableFrame::RestyleTable() { // re-sync MathML specific style data that may have changed MapAllAttributesIntoCSS(this); // Explicitly request a re-resolve and reflow in our subtree to pick up any changes PresContext()->RestyleManager()-> PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree, nsChangeHint_AllReflowHints); } // -------- // implementation of nsMathMLmtrFrame nsIFrame* NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsMathMLmtrFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame) nsMathMLmtrFrame::~nsMathMLmtrFrame() { } NS_IMETHODIMP nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType) { // Attributes specific to : // groupalign : Not yet supported. // rowalign : Here // columnalign : Here nsPresContext* presContext = PresContext(); if (aAttribute != nsGkAtoms::rowalign_ && aAttribute != nsGkAtoms::columnalign_) { return NS_OK; } presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute)); bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_); // Reparse the new attribute. ParseFrameAttribute(this, aAttribute, allowMultiValues); // Explicitly request a reflow in our subtree to pick up any changes presContext->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); return NS_OK; } // -------- // implementation of nsMathMLmtdFrame nsIFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsMathMLmtdFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame) nsMathMLmtdFrame::~nsMathMLmtdFrame() { } int32_t nsMathMLmtdFrame::GetRowSpan() { int32_t rowspan = 1; // Don't look at the content's rowspan if we're not an mtd or a pseudo cell. if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) { nsAutoString value; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value); if (!value.IsEmpty()) { nsresult error; rowspan = value.ToInteger(&error); if (NS_FAILED(error) || rowspan < 0) rowspan = 1; rowspan = std::min(rowspan, MAX_ROWSPAN); } } return rowspan; } int32_t nsMathMLmtdFrame::GetColSpan() { int32_t colspan = 1; // Don't look at the content's colspan if we're not an mtd or a pseudo cell. if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) { nsAutoString value; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value); if (!value.IsEmpty()) { nsresult error; colspan = value.ToInteger(&error); if (NS_FAILED(error) || colspan < 0 || colspan > MAX_COLSPAN) colspan = 1; } } return colspan; } NS_IMETHODIMP nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType) { // Attributes specific to : // groupalign : Not yet supported // rowalign : here // columnalign : here // rowspan : here // columnspan : here if (aAttribute == nsGkAtoms::rowalign_ || aAttribute == nsGkAtoms::columnalign_) { nsPresContext* presContext = PresContext(); presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute)); // Reparse the attribute. ParseFrameAttribute(this, aAttribute, false); return NS_OK; } if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::columnspan_) { // use the naming expected by the base class if (aAttribute == nsGkAtoms::columnspan_) aAttribute = nsGkAtoms::colspan; return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); } return NS_OK; } uint8_t nsMathMLmtdFrame::GetVerticalAlign() const { // Set the default alignment in case no alignment was specified uint8_t alignment = nsTableCellFrame::GetVerticalAlign(); nsTArray* alignmentList = FindCellProperty(this, RowAlignProperty()); if (alignmentList) { int32_t rowIndex; GetRowIndex(rowIndex); // If the row number is greater than the number of provided rowalign values, // we simply repeat the last value. if (rowIndex < (int32_t)alignmentList->Length()) alignment = alignmentList->ElementAt(rowIndex); else alignment = alignmentList->ElementAt(alignmentList->Length() - 1); } return alignment; } nsresult nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame, nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { aLists.BorderBackground()->AppendNewToTop(new (aBuilder) nsDisplaymtdBorder(aBuilder, this)); return NS_OK; } // -------- // implementation of nsMathMLmtdInnerFrame NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame) NS_QUERYFRAME_ENTRY(nsIMathMLFrame) NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) nsIFrame* NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsMathMLmtdInnerFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame) nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext* aContext) : nsBlockFrame(aContext) { // Make a copy of the parent nsStyleText for later modificaiton. mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText()); } nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame() { mUniqueStyleText->Destroy(PresContext()); } NS_IMETHODIMP nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { // Let the base class do the reflow nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); // more about and later // ... return rv; } const nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout() { // Set the default alignment in case nothing was specified uint8_t alignment = StyleText()->mTextAlign; nsTArray* alignmentList = FindCellProperty(this, ColumnAlignProperty()); if (alignmentList) { nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent(); int32_t columnIndex; cellFrame->GetColIndex(columnIndex); // If the column number is greater than the number of provided columalign // values, we simply repeat the last value. if (columnIndex < (int32_t)alignmentList->Length()) alignment = alignmentList->ElementAt(columnIndex); else alignment = alignmentList->ElementAt(alignmentList->Length() - 1); } mUniqueStyleText->mTextAlign = alignment; return mUniqueStyleText; } /* virtual */ void nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) { nsBlockFrame::DidSetStyleContext(aOldStyleContext); mUniqueStyleText->Destroy(PresContext()); mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText()); }