/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
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<int8_t>();
}
// 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;
}
// Each rowalign='top bottom' or columnalign='left right center' (from // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is // stored in the property table. Row/Cell frames query the property table // to see what values apply to them.
/* 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 hierarchy for a property * and returns its value. The function stops searching after checking a <mtable> * frame.
*/ static nsTArray<int8_t>* FindCellProperty( const nsIFrame* aCellFrame, const FramePropertyDescriptor<nsTArray<int8_t>>* aFrameProperty) { const nsIFrame* currentFrame = aCellFrame;
nsTArray<int8_t>* propertyData = nullptr;
while (currentFrame) {
propertyData = currentFrame->GetProperty(aFrameProperty); bool frameIsTable = (currentFrame->IsTableFrame());
if (propertyData || frameIsTable) {
currentFrame = nullptr; // A null frame pointer exits the loop
} else {
currentFrame = currentFrame->GetParent(); // Go to the parent frame
}
}
// 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.
uint32_t listLength = rowLinesList->Length(); if (rowIndex < listLength) {
aStyleBorder.SetBorderStyle(
eSideTop, static_cast<StyleBorderStyle>(rowLinesList->ElementAt(rowIndex - 1)));
} else {
aStyleBorder.SetBorderStyle(eSideTop, static_cast<StyleBorderStyle>(
rowLinesList->ElementAt(listLength - 1)));
}
aStyleBorder.SetBorderWidth(eSideTop, borderWidth, a2d);
}
// 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.
uint32_t listLength = columnLinesList->Length(); if (columnIndex < listLength) {
aStyleBorder.SetBorderStyle(
eSideLeft, static_cast<StyleBorderStyle>(
columnLinesList->ElementAt(columnIndex - 1)));
} else {
aStyleBorder.SetBorderStyle(
eSideLeft, static_cast<StyleBorderStyle>(
columnLinesList->ElementAt(listLength - 1)));
}
aStyleBorder.SetBorderWidth(eSideLeft, borderWidth, a2d);
}
}
/* * 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 final : public nsDisplayBorder { public:
nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
: nsDisplayBorder(aBuilder, aFrame) {}
if (!attrValue.IsEmpty()) {
nsTArray<int8_t>* 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!");
aFrame->SetProperty(AttributeToProperty(aAttribute), valueList);
} else {
ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
}
}
}
// rowspacing // // Specifies the distance between successive rows in an mtable. Multiple // lengths can be specified, each corresponding to its respective position // between rows. For example: // // [ROW_0] // rowspace_0 // [ROW_1] // rowspace_1 // [ROW_2] // // If the number of row gaps exceeds the number of lengths specified, the final // specified length is repeated. Additional lengths are ignored. // // values: (length)+ // default: 1.0ex // // Unitless values are permitted and provide a multiple of the default value // Negative values are forbidden. //
// columnspacing // // Specifies the distance between successive columns in an mtable. Multiple // lengths can be specified, each corresponding to its respective position // between columns. For example: // // [COLUMN_0] columnspace_0 [COLUMN_1] columnspace_1 [COLUMN_2] // // If the number of column gaps exceeds the number of lengths specified, the // final specified length is repeated. Additional lengths are ignored. // // values: (length)+ // default: 0.8em // // Unitless values are permitted and provide a multiple of the default value // Negative values are forbidden. //
// framespacing // // Specifies the distance between the mtable and its frame (if any). The // first value specified provides the spacing between the left and right edge // of the table and the frame, the second value determines the spacing between // the top and bottom edges and the frame. // // An error is reported if only one length is passed. Any additional lengths // are ignored // // values: length length // default: 0em 0ex If frame attribute is "none" or not specified, // 0.4em 0.5ex otherwise // // Unitless values are permitted and provide a multiple of the default value // Negative values are forbidden. //
// map all attributes 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. staticvoid MapAllAttributesIntoCSS(nsMathMLmtableFrame* aTableFrame) { // Map mtable rowalign & rowlines.
ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
// mtable is simple and only has one (pseudo) row-group
nsIFrame* rgFrame = aTableFrame->PrincipalChildList().FirstChild(); if (!rgFrame || !rgFrame->IsTableRowGroupFrame()) { return;
}
// 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
staticvoid 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(u"top")) {
len = 3; // 3 is the length of 'top'
aAlign = eAlign_top;
} elseif (0 == aValue.Find(u"bottom")) {
len = 6; // 6 is the length of 'bottom'
aAlign = eAlign_bottom;
} elseif (0 == aValue.Find(u"center")) {
len = 6; // 6 is the length of 'center'
aAlign = eAlign_center;
} elseif (0 == aValue.Find(u"baseline")) {
len = 8; // 8 is the length of 'baseline'
aAlign = eAlign_baseline;
} elseif (0 == aValue.Find(u"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;
}
}
}
// -------- // implementation of nsMathMLmtableWrapperFrame
nsresult nsMathMLmtableWrapperFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) { // Attributes specific to <mtable>: // frame : in mathml.css // framespacing : here // 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 : here // columnalign : here // columnlines : here // columnspacing : here
// mtable is simple and only has one (pseudo) row-group inside our inner-table
nsIFrame* tableFrame = mFrames.FirstChild();
NS_ASSERTION(tableFrame && tableFrame->IsTableFrame(), "should always have an inner table frame");
nsIFrame* rgFrame = tableFrame->PrincipalChildList().FirstChild(); if (!rgFrame || !rgFrame->IsTableRowGroupFrame()) { return NS_OK;
}
// align - just need to issue a dirty (resize) reflow command if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::align) {
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::None,
NS_FRAME_IS_DIRTY); return NS_OK;
}
// ...and the other attributes affect rows or columns in one way or another
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::rowalign_ ||
aAttribute == nsGkAtoms::rowlines_ ||
aAttribute == nsGkAtoms::columnalign_ ||
aAttribute == nsGkAtoms::columnlines_)) { // clear any cached property list for this table
tableFrame->RemoveProperty(AttributeToProperty(aAttribute)); // Reparse the new attribute on the table.
ParseFrameAttribute(tableFrame, aAttribute, true);
PresShell()->FrameNeedsReflow( this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY); return NS_OK;
}
// Skip nsTableWrapperFrame::AttributeChanged, mtable does not share more // attributes with table. return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
// 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->IsTableFrame(), "should always have an inner table frame");
nsIFrame* rgFrame = tableFrame->PrincipalChildList().FirstChild(); if (!rgFrame || !rgFrame->IsTableRowGroupFrame()) { return nullptr;
} for (nsIFrame* rowFrame : rgFrame->PrincipalChildList()) { if (aRowIndex == 0) {
DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TableRow); if (!rowFrame->IsTableRowFrame()) { return nullptr;
}
void nsMathMLmtableWrapperFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) {
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
nsAutoString value; // we want to return a table that is anchored according to the align attribute
nsTableWrapperFrame::Reflow(aPresContext, aDesiredSize, aReflowInput,
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 <mtable>
int32_t rowIndex = 0;
eAlign tableAlign = eAlign_axis;
mContent->AsElement()->GetAttr(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;
WritingMode wm = aDesiredSize.GetWritingMode();
nscoord blockSize = aDesiredSize.BSize(wm);
nsIFrame* rowFrame = nullptr; if (rowIndex) {
rowFrame = GetRowFrameAt(rowIndex); if (rowFrame) { // translate the coordinates to be relative to us and in our writing mode
nsIFrame* frame = rowFrame;
LogicalRect rect(wm, frame->GetRect(),
aReflowInput.ComputedSizeAsContainerIfConstrained());
blockSize = rect.BSize(wm); do {
nsIFrame* parent = frame->GetParent();
dy += frame->BStart(wm, parent->GetSize());
frame = parent;
} while (frame != this);
}
} switch (tableAlign) { case eAlign_top:
aDesiredSize.SetBlockStartAscent(dy); break; case eAlign_bottom:
aDesiredSize.SetBlockStartAscent(dy + blockSize); break; case eAlign_center:
aDesiredSize.SetBlockStartAscent(dy + blockSize / 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.SetBlockStartAscent(dy + rowAscent); break;
}
} // in other situations, fallback to center
aDesiredSize.SetBlockStartAscent(dy + blockSize / 2); break; case eAlign_axis: default: { // XXX should instead use style data from the row of reference here ?
RefPtr<nsFontMetrics> fm =
nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
nscoord axisHeight;
GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm,
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.SetBlockStartAscent(dy + rowAscent); break;
}
} // in other situations, fallback to using half of the height
aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight);
}
}
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(), RestyleHint::RestyleSubtree(),
nsChangeHint_AllReflowHints);
}
nscoord nsMathMLmtableFrame::GetColSpacing(int32_t aColIndex) { if (mUseCSSSpacing) { return nsTableFrame::GetColSpacing(aColIndex);
} if (!mColSpacing.Length()) {
NS_ERROR("mColSpacing should not be empty"); return 0;
} if (aColIndex < 0 || aColIndex >= GetColCount()) {
NS_ASSERTION(aColIndex == -1 || aColIndex == GetColCount(), "Desired column beyond bounds of table and border"); return mFrameSpacingX;
} if ((uint32_t)aColIndex >= mColSpacing.Length()) { return mColSpacing.LastElement();
} return mColSpacing.ElementAt(aColIndex);
}
nscoord nsMathMLmtableFrame::GetColSpacing(int32_t aStartColIndex,
int32_t aEndColIndex) { if (mUseCSSSpacing) { return nsTableFrame::GetColSpacing(aStartColIndex, aEndColIndex);
} if (aStartColIndex == aEndColIndex) { return 0;
} if (!mColSpacing.Length()) {
NS_ERROR("mColSpacing should not be empty"); return 0;
}
nscoord space = 0; if (aStartColIndex < 0) {
NS_ASSERTION(aStartColIndex == -1, "Desired column beyond bounds of table and border");
space += mFrameSpacingX;
aStartColIndex = 0;
} if (aEndColIndex >= GetColCount()) {
NS_ASSERTION(aEndColIndex == GetColCount(), "Desired column beyond bounds of table and border");
space += mFrameSpacingX;
aEndColIndex = GetColCount();
} // Only iterate over column spacing when there is the potential to vary
int32_t min = std::min(aEndColIndex, (int32_t)mColSpacing.Length()); for (int32_t i = aStartColIndex; i < min; i++) {
space += mColSpacing.ElementAt(i);
} // The remaining values are constant. Note that if there are more // column spacings specified than there are columns, LastElement() will be // multiplied by 0, so it is still safe to use.
space += (aEndColIndex - min) * mColSpacing.LastElement(); return space;
}
nscoord nsMathMLmtableFrame::GetRowSpacing(int32_t aRowIndex) { if (mUseCSSSpacing) { return nsTableFrame::GetRowSpacing(aRowIndex);
} if (!mRowSpacing.Length()) {
NS_ERROR("mRowSpacing should not be empty"); return 0;
} if (aRowIndex < 0 || aRowIndex >= GetRowCount()) {
NS_ASSERTION(aRowIndex == -1 || aRowIndex == GetRowCount(), "Desired row beyond bounds of table and border"); return mFrameSpacingY;
} if ((uint32_t)aRowIndex >= mRowSpacing.Length()) { return mRowSpacing.LastElement();
} return mRowSpacing.ElementAt(aRowIndex);
}
nscoord nsMathMLmtableFrame::GetRowSpacing(int32_t aStartRowIndex,
int32_t aEndRowIndex) { if (mUseCSSSpacing) { return nsTableFrame::GetRowSpacing(aStartRowIndex, aEndRowIndex);
} if (aStartRowIndex == aEndRowIndex) { return 0;
} if (!mRowSpacing.Length()) {
NS_ERROR("mRowSpacing should not be empty"); return 0;
}
nscoord space = 0; if (aStartRowIndex < 0) {
NS_ASSERTION(aStartRowIndex == -1, "Desired row beyond bounds of table and border");
space += mFrameSpacingY;
aStartRowIndex = 0;
} if (aEndRowIndex >= GetRowCount()) {
NS_ASSERTION(aEndRowIndex == GetRowCount(), "Desired row beyond bounds of table and border");
space += mFrameSpacingY;
aEndRowIndex = GetRowCount();
} // Only iterate over row spacing when there is the potential to vary
int32_t min = std::min(aEndRowIndex, (int32_t)mRowSpacing.Length()); for (int32_t i = aStartRowIndex; i < min; i++) {
space += mRowSpacing.ElementAt(i);
} // The remaining values are constant. Note that if there are more // row spacings specified than there are row, LastElement() will be // multiplied by 0, so it is still safe to use.
space += (aEndRowIndex - min) * mRowSpacing.LastElement(); return space;
}
nsresult nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) { // Attributes specific to <mtr>: // groupalign : Not yet supported. // rowalign : Here // columnalign : Here
if (aNameSpaceID != kNameSpaceID_None ||
(aAttribute != nsGkAtoms::rowalign_ &&
aAttribute != nsGkAtoms::columnalign_)) { // Skip nsTableCellFrame::AttributeChanged, mtr does not share any attribute // with tr. return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
// Reparse the new attribute.
ParseFrameAttribute(this, aAttribute, allowMultiValues);
// Explicitly request a reflow in our subtree to pick up any changes
PresShell()->FrameNeedsReflow( this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
// We want to use the ancestor <math> element's font inflation to avoid // individual cells having their own varying font inflation.
RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
}
nsresult nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) { // Attributes specific to <mtd>: // groupalign : Not yet supported // rowalign : here // columnalign : here // rowspan : here // columnspan : here
// Reparse the attribute.
ParseFrameAttribute(this, aAttribute, false); return NS_OK;
}
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::rowspan ||
aAttribute == nsGkAtoms::columnspan_)) { // nsTableCellFrame takes care of renaming columnspan to colspan. return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
// Skip nsTableCellFrame::AttributeChanged, mtd does not share more attributes // with td. return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
StyleVerticalAlignKeyword nsMathMLmtdFrame::GetVerticalAlign() const { // Set the default alignment in case no alignment was specified auto alignment = nsTableCellFrame::GetVerticalAlign();
if (alignmentList) {
uint32_t rowIndex = RowIndex();
// If the row number is greater than the number of provided rowalign values, // we simply repeat the last value. returnstatic_cast<StyleVerticalAlignKeyword>(
(rowIndex < alignmentList->Length())
? alignmentList->ElementAt(rowIndex)
: alignmentList->LastElement());
}
nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext)
: nsBlockFrame(aStyle, aPresContext, kClassID) // Make a copy of the parent nsStyleText for later modification.
,
mUniqueStyleText(MakeUnique<nsStyleText>(*StyleText())) {}
void nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) { // Let the base class do the reflow
nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
// more about <maligngroup/> and <malignmark/> later // ...
}
const nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout() { // Set the default alignment in case nothing was specified auto alignment = uint8_t(StyleText()->mTextAlign);
// If the column number is greater than the number of provided columalign // values, we simply repeat the last value. if (columnIndex < alignmentList->Length()) {
alignment = alignmentList->ElementAt(columnIndex);
} else {
alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.