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