Files
palemoon27/layout/mathml/nsMathMLmtableFrame.cpp
T
roytam1 b3541682d0 import changes from `dev' branch of rmottola/Arctic-Fox:
- bug 1157569 part 1 - Rename BC_BORDER_{TOP,RIGHT,BOTTOM,LEFT}_HALF* to BC_BORDER_{START,END}_HALF*. r=roc (2ef82ce76)
- Bug 1157569 part 2 - Convert output parameters of GetColorAndStyle/GetPaintStyleInfo in nsTableFrame to use pointers. r=roc (c58bbe734)
- Bug 1157569 part 3 - Merge two GetColorAndStyle functions in nsTableFrame. r=roc (009df4d97)
- Bug 1157569 part 4 - Replace mTableIsLTR with mTableWM in BCMapCellInfo & BCPaintBorderIterator. r=roc (9784a46eb)
- Bug 1157569 part 5 - Convert GetColorAndStyle, GetPaintStyleInfo, and CompareBorders in nsTableFrame to accept writing mode and logical side. r=roc (aaddc4a80)
- Bug 1157569 part 6 - Rename methods and fields in BCMapCellInfo from physicals to logicals. r=roc (aeaa0b18c)
- Bug 1157569 part 7 - Rename methods and fields in nsTableColFrame from physicals to logicals. r=roc (fd4e1e667)
- Bug 1157569 part 8 - Move some code in BCPaintBorderIterator::SetDamageArea for less computation. r=roc (634f19ca7)
- missing bit of Bug 895096 - Part 1 (a96e3d35c)
- Bug 1157569 part 9 - Rename methods and fields in nsTableRowFrame from physicals to logicals. r=roc (d097f1e4f)
- Bug 1157569 part 10 - Add operator+= for LogicalMargin. r=jfkthame (c651b685f)
- Bug 1159101 part 4 - Override GetWritingMode() in nsTable{{Col,Row}{,Group},Cell}Frame. r=roc (34d90d10b)
- Bug 1159101 part 5 - Initialize writing mode of desired mode of table cell frame from its table frame on a CLOSED TREE. r=roc (8bc6978ca)
- Bug 1157569 part 11 - Rename methods and fields in nsTableCellFrame from physicals to logicals. r=roc (bcfd4514b)
- Bug 1157569 part 12 - Remove useless m{Start,End}Side from BCMapCellInfo. r=roc (1df59d670)
- Bug 1159127 - Always redirect inserting col group frame to first-in-flow table frame. r=mats (2e41bbeeb)
- Bug 1167765: Misc whitespace cleanup in nsTableFrame.cpp. whitespace/rewrapping-only, no review, DONTBUILD (f256939c9)
- Bug 1167696 part 1: Remove nsTableReflowState's unused constructor & nsPresContext arg. r=mats (5b8beadb9)
- Bug 1167696 part 2: Remove nsTableReflowState's frame constructor-arg; get it from passed-in reflow state instead. r=mats (205dad74f)
- Bug 1155412: Use mozilla::Maybe instead of hardcoded placement-new, for reflow state created in nsTableOuterFrame::OuterBeginReflowChild() and used by its caller. r=dbaron (ebf9e9891)
- Bug 1147834: Use abstract coordinates in nsHTMLReflowState, r=jfkthame (30e644a14)
- Bug 1169432 part 2: Refactor nsTableFrame::Init. r=mats (779928597)
- Bug 1169432 part 3: Use nsTArray::Contains instead of nsTArray::IndexOf(), for brevity, in nsTableFrame. r=mats (aa6aee3c4)
- Bug 1169432 part 4: Convert NS_ASSERTION(false, ...) to NS_ERROR(...) in nsTableFrame. r=mats (9cbb2f53f)
- Bug 1169432 part 5: Use range-based 'for' loops when walking frame lists, in nsTableFrame. r=mats (bd4e7d0d4)
- Bug 1171328 - Convert nsTableFrame::GetChildAreaOffset() and its friends to use LogicalMargin. r=dholbert (196955d0c)
- Remove unneeded code from SmoothScrollAnimation. (bug 1139220 part 4, r=kats) (68c6af9f8)
- Use stronger typing for GetScrollWheelDelta. (bug 1139220 part 5, r=kats) (4669d1f38)
- When APZ is enabled, only apply the root scroll delta override to root frames. (bug 1155800, r=kats) (2a738ee82)
- Bug 1168629. Clear frame metrics on the root layer if we aren't setting new frame metrics because the layer may be recycled and still have frame metrics from last time. r=mstange (dc0889fae)
- Bug 1158424 - Rename FrameMetrics::mIsRoot to mIsRootContent. r=kats (0005fb016)
- Bug 1148078: Replace the word "Logical" with "Flex-Relative" in some vars & functions in flexbox layout. r=mats (601497c55)
- Bug 1148298 part 1: Convert most flexbox-layout IsAxisHorizontal() calls into IsMainAxisHorizontal()/IsCrossAxisHorizontal(). r=mats (ff2aaa5b3)
- Bug 1148298 part 3: Make flexbox helper GetBaselineOffsetFromOuterCrossEdge take a FlexboxAxisTracker instead of an explicit axis. r=mats (009dd9f7e)
- Bug 1148298 part 4: Convert flexbox helper-function IsAxisHorizontal() to be a private implementation detail of FlexboxAxisTracker. r=mats (901f830d6)
- Bug 1148298 part 2: Replace flexbox static helper-function GetSizeProBug 1148298 part 2: Replace flexbox static helper-function GetSizePro (3d2940f88)
- Bug 1158290 part 1: Use AvailableBSize instead of AvailableHeight, throughout flex layout. r=mats (49e2075f8)
- Bug 1148294 part 1: Determine flex axes (in FlexboxAxisTracker) using the flex container's writing mode. r=mats (67f12cf58)
- Bug 1148294 part 2: Add one reftest for writing-mode & direction properties' influence on flex axes. (85e0ede9d)
- Bug 1148294 part 3: Add more reftests for writing-mode & direction properties' influence on flex axes (as modified copies of first test). (fac965a24)
- Bug 1148294 followup: fix spec links in reftests flexbox-writing-mode-* to point to TR instead of ED spec version. (no review) (84333c728)
- Bug 1158290 part 2: Use logical (not physical) skipsides & borderpadding when chipping away from available BSize. r=mats (987b4edfa)
- Bug 1152913 part 1: Make FlexboxAxisTracker store the WritingMode & representation of how flex axes map to it. r=mats (675737981)
- Bug 1152951: Delete the copy-constructor and reassignment operator for FlexboxAxisTracker & PositionTracker state-management classes. r=mats (71c88e7e9)
- Bug 1152913 part 2: Convert flexbox 'IsAxisHorizontal' checks to use new logical-axis member data. r=mats (122b434a5)
- Bug 1158290 part 3: Convert GenerateFlexLines to use logical axes & sizes. r=mats (367cb1c01)
- Bug 1158290 part 4: Clarify documentation & naming of ClampFlexContainerMainSize (now called ResolveFlexContainerMainSize). r=mats (5af11ff23)
- Bug 1149383 part 1: Make flexbox writing-mode tests use different widths vs. heights to reveal bug with vertical writing-modes. (test-only) (c4782feaa)
- Bug 1149383 part 2: Reorder width/height decls in flexbox-writing-mode reftests for consistency. (test-only) (bba907b70)
- Bug 1158290 part 5: Use logical coords & axes in methods that computeBug 1158290 part 5: Use logical coords & axes in methods that compute (b78726528)
- Bug 1158290 part 6: Use new GET_[MAIN|CROSS]_COMPONENT_LOGICAL macros, to reduce explicit width/height usage. r=mats (25d2e20a3)
- Bug 1152913 part 3: Make flexbox's PositionTracker classes track whether axis is reversed, & directly convert flex item final sizes/positions to logical coords. r=mats (1feee7e6d)
- Bug 1174507 - Replace the frame flag CONTAINS_RELATIVE_HEIGHT with CONTAINS_RELATIVE_BSIZE, and adjust callsites appropriately. r=dholbert (0e981d5ac)
- Clip async scrollframes based on the displaylist clip, not the composition bounds. (bug 1148582 part 4, r=tn) (f2b1196ed)
- Bug 1148582 - Add mask layers to FrameMetrics for ancestor scroll frame clips. r=mattwoodrow (008239305)
- Bug 1148582 - Factor mask layer creation out of ContainerState::SetupMaskLayer. r=mstange (1d739614e)
- piece of Bug 1138442 (e98de18dd)
- Bug 1173307 - Convert nsTableCellFrame to work with logical coordinates. r=dholbert (81d0aa811)
- Bug 1174700 - patch 1 - Convert nsTableRowFrame and nsTableRowGroupFrame to work with logical coordinates. r=dholbert (09ee9c814)
- Bug 1174700 - patch 2 - Convert nsTableFrame::SetColumnDimensions to work with logical-order iteration over the table, so that column coordinates and collapsed borders are correct. r=dholbert (7957e377b)
- Bug 1129040 - Provide a way for content processes to query the chrome side blocklist service. r=billm (5ea750b8b)
- Bug 1174711 - patch 1 - Rename nsIPercentHeightObserver to nsIPercentBSizeObserver, and update related frame methods to match. r=dholbert (7a17fbe4f)
- Bug 1174711 - patch 2 - Convert nsTableFrame to work with logical coordinates. r=dholbert (329776abe)
- Bug 1174711 - patch 3 - Rename a couple more frame-state bits from physical to logical. r=dholbert (d61c9624d)
- Bug 1174711 - patch 4 - Rename mSpecialHeightReflow to mSpecialBSizeReflow, and update comments to match. r=dholbert (49969e512)
- Bug 1174711 patch 5 - Copy inline-size rather than width from prev-in-flow when initializing nsTableFrame. r=dholbert (7afb6c80a)
- pointer style (52cd6f9af)
- pointer style (ba99b3f74)
- Bug 1149797 - Use the loop's *static* block object when freshening a loop's block object, then copy in values from the old cloned block. Using the old cloned block directly isn't valid when the cloned block might be extended with additional variables created by eval or added by nested function statements. r=shu (deba22790)
- Bug 1165486 - Cleanup: use standard object allocation functions when allocating scope objects. (r=terrence) (b35a4d7aa)
- pointer style (74ea6d085)
- Bug 1165486 - Add StaticNonSyntacticScopeObjects and teach scope iterators about it. (r=luke) (18a526ed3)
- Bug 1144371: Implement DEBUG-only JS shell function |dumpStringRepresentation|. r=jandem (35524cf42)
- Bug 1148963 - OdinMonkey: add CompileOptions::lazyParsingDisabled and testing function setDiscardSource (r=bz) (53f9c8fe6)
- pointer style (edaa05d69)
- Bug 1165486 - Rename hasPollutedGlobalScope to hasNonSyntacticScope. (r=luke) (505fc8d05)
- pointer style (3c4435038)
- Bug 1165486 - Remove PollutedGlobalScopeOption in favor of using the static scope chain to detect non-syntactic scopes. (r=luke) (73ca126f5)
- Bug 1150106 - Async loadSubscript - Part 1, add `async` to loadSubscriptWithOptions r=bholley,mcrr8 (a0ffeed0d)
- Bug 1150106 - Add an async version of loadSubscript - Part 2, tests r=bholley (6aeaa93e4)
- Bug 1165486 - Use JS::CompileForNonSyntacticScope in Gecko where we used to set polluted global scope. (r=bz, rs=bholley) (073332ac9)
- Bug 1165486 - Split JS::Compile into JS::Compile and JS::CompileForNonSyntacticScope. (r=luke) (0b82c73c4)
- Bug 1165486 - Restructure function and script cloning in light of PollutingGlobal scope changes. (r=Waldo) (b4e39652c)
- Bug 1141865 - Part 7: Make new.target work in generator functions. (r=jorendorff, r=jandem) (a9f5ce1ad)
- Bug 1165486 - Detect with scopes at parse time using the static scope chain for non-function scripts. Also cache static scope properties on SharedGlobalContext. (r=efaust) (a7d4283cb)
- Bug 1176070 - Optimize nsTableFrame's FirstInFlow() lookups from calls to GetColumnISize(). r=dholbert (beed47b93)
- fix misspatch of Bug 1141862 - Part 6 (d091f70a2)
- Bug 1169736 - Temporarily disallow eval and arrow functions inside derived class constructors. (r=jorendorff) (6cf8a2db1)
- Bug 1150855 - Disallow method syntax without curly brackets. r=efaust (26086ee95)
- part of Bug 1150855 - Remove uses of the curly syntax. (bfe86ea19)
- Bug 1169853 - Require semicolon after export default AssignmentExpression. r=shu (580ff7e30)
- Bug 1174009 - Remove a couple of unused AutoRooter types; r=sfink (113aee16a)
2020-12-21 14:13:41 +08:00

1332 lines
43 KiB
C++

/* -*- 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 "nsNameSpaceManager.h"
#include "nsRenderingContext.h"
#include "nsCSSRendering.h"
#include "nsMathMLElement.h"
#include "nsTArray.h"
#include "nsTableFrame.h"
#include "celldata.h"
#include "RestyleManager.h"
#include <algorithm>
#include "nsIScriptError.h"
#include "nsContentUtils.h"
using namespace mozilla;
//
// <mtable> -- 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<int8_t>*
ExtractStyleValues(const nsAString& aString, nsIAtom* aAttribute,
bool aAllowMultiValues)
{
nsTArray<int8_t>* 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<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;
}
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->NodeInfo()->NameAtom()->GetUTF16String() };
return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("Layout: MathML"),
content->OwnerDoc(),
nsContentUtils::eMATHML_PROPERTIES,
"AttributeParsingError", params, 3);
}
// 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.
NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DeleteValue<nsTArray<int8_t>>)
NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DeleteValue<nsTArray<int8_t>>)
NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DeleteValue<nsTArray<int8_t>>)
NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DeleteValue<nsTArray<int8_t>>)
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 <mtable>
* frame.
*/
static nsTArray<int8_t>*
FindCellProperty(const nsIFrame* aCellFrame,
const FramePropertyDescriptor* aFrameProperty)
{
const nsIFrame* currentFrame = aCellFrame;
nsTArray<int8_t>* propertyData = nullptr;
while (currentFrame) {
FrameProperties props = currentFrame->Properties();
propertyData = static_cast<nsTArray<int8_t>*>(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;
}
static void
ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
nsStyleBorder& aStyleBorder)
{
int32_t rowIndex;
int32_t columnIndex;
aFrame->GetRowIndex(rowIndex);
aFrame->GetColIndex(columnIndex);
nscoord borderWidth =
aFrame->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN];
nsTArray<int8_t>* rowLinesList =
FindCellProperty(aFrame, RowLinesProperty());
nsTArray<int8_t>* columnLinesList =
FindCellProperty(aFrame, 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) {
aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
rowLinesList->ElementAt(rowIndex - 1));
} else {
aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
rowLinesList->ElementAt(listLength - 1));
}
aStyleBorder.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) {
aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
columnLinesList->ElementAt(columnIndex - 1));
} else {
aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
columnLinesList->ElementAt(listLength - 1));
}
aStyleBorder.SetBorderWidth(NS_SIDE_LEFT, borderWidth);
}
}
static nsMargin
ComputeBorderOverflow(nsMathMLmtdFrame* aFrame, nsStyleBorder aStyleBorder)
{
nsMargin overflow;
int32_t rowIndex;
int32_t columnIndex;
nsTableFrame* table = aFrame->GetTableFrame();
aFrame->GetCellIndexes(rowIndex, columnIndex);
if (!columnIndex) {
overflow.left = table->GetColSpacing(-1);
overflow.right = table->GetColSpacing(0) / 2;
} else if (columnIndex == table->GetColCount() - 1) {
overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
overflow.right = table->GetColSpacing(columnIndex + 1);
} else {
overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
overflow.right = table->GetColSpacing(columnIndex) / 2;
}
if (!rowIndex) {
overflow.top = table->GetRowSpacing(-1);
overflow.bottom = table->GetRowSpacing(0) / 2;
} else if (rowIndex == table->GetRowCount() - 1) {
overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
overflow.bottom = table->GetRowSpacing(rowIndex + 1);
} else {
overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
overflow.bottom = table->GetRowSpacing(rowIndex) / 2;
}
return overflow;
}
/*
* 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 nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
{
*aSnap = true;
nsStyleBorder styleBorder = *mFrame->StyleBorder();
nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
ApplyBorderToStyle(frame, styleBorder);
nsRect bounds = CalculateBounds(styleBorder);
nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
bounds.Inflate(overflow);
return bounds;
}
virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override
{
nsStyleBorder styleBorder = *mFrame->StyleBorder();
nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
ApplyBorderToStyle(frame, styleBorder);
nsRect bounds = nsRect(ToReferenceFrame(), mFrame->GetSize());
nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
bounds.Inflate(overflow);
nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx,
mFrame, mVisibleRect,
bounds,
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<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!");
FrameProperties props = aFrame->Properties();
props.Set(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.
//
static const float kDefaultRowspacingEx = 1.0f;
static const float kDefaultColumnspacingEm = 0.8f;
static const float kDefaultFramespacingArg0Em = 0.4f;
static const float kDefaultFramespacingArg1Ex = 0.5f;
static void
ExtractSpacingValues(const nsAString& aString,
nsIAtom* aAttribute,
nsTArray<nscoord>& aSpacingArray,
nsIFrame* aFrame,
nscoord aDefaultValue0,
nscoord aDefaultValue1,
float aFontSizeInflation)
{
nsPresContext* presContext = aFrame->PresContext();
nsStyleContext* styleContext = aFrame->StyleContext();
const char16_t* start = aString.BeginReading();
const char16_t* end = aString.EndReading();
int32_t startIndex = 0;
int32_t count = 0;
int32_t elementNum = 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) {
const nsAString& str = Substring(aString, startIndex, count);
nsAutoString valueString;
valueString.Assign(str);
nscoord newValue;
if (aAttribute == nsGkAtoms::framespacing_ && elementNum) {
newValue = aDefaultValue1;
} else {
newValue = aDefaultValue0;
}
nsMathMLFrame::ParseNumericValue(valueString, &newValue,
nsMathMLElement::PARSE_ALLOW_UNITLESS,
presContext, styleContext,
aFontSizeInflation);
aSpacingArray.AppendElement(newValue);
startIndex += count;
count = 0;
elementNum++;
}
}
}
static void
ParseSpacingAttribute(nsMathMLmtableFrame* aFrame, nsIAtom* aAttribute)
{
NS_ASSERTION(aAttribute == nsGkAtoms::rowspacing_ ||
aAttribute == nsGkAtoms::columnspacing_ ||
aAttribute == nsGkAtoms::framespacing_,
"Non spacing attribute passed");
nsAutoString attrValue;
nsIContent* frameContent = aFrame->GetContent();
frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
if (nsGkAtoms::framespacing_ == aAttribute) {
nsAutoString frame;
frameContent->GetAttr(kNameSpaceID_None, nsGkAtoms::frame, frame);
if (frame.IsEmpty() || frame.EqualsLiteral("none")) {
aFrame->SetFrameSpacing(0, 0);
return;
}
}
nscoord value;
nscoord value2;
// Set defaults
float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
fontSizeInflation);
if (nsGkAtoms::rowspacing_ == aAttribute) {
value = kDefaultRowspacingEx * fm->XHeight();
value2 = 0;
} else if (nsGkAtoms::columnspacing_ == aAttribute) {
value = kDefaultColumnspacingEm * fm->EmHeight();
value2 = 0;
} else {
value = kDefaultFramespacingArg0Em * fm->EmHeight();
value2 = kDefaultFramespacingArg1Ex * fm->XHeight();
}
nsTArray<nscoord> valueList;
ExtractSpacingValues(attrValue, aAttribute, valueList, aFrame, value, value2,
fontSizeInflation);
if (valueList.Length() == 0) {
if (frameContent->HasAttr(kNameSpaceID_None, aAttribute)) {
ReportParseError(aFrame, aAttribute->GetUTF16String(),
attrValue.get());
}
valueList.AppendElement(value);
}
if (aAttribute == nsGkAtoms::framespacing_) {
if (valueList.Length() == 1) {
if(frameContent->HasAttr(kNameSpaceID_None, aAttribute)) {
ReportParseError(aFrame, aAttribute->GetUTF16String(),
attrValue.get());
}
valueList.AppendElement(value2);
} else if (valueList.Length() != 2) {
ReportParseError(aFrame, aAttribute->GetUTF16String(),
attrValue.get());
}
}
if (aAttribute == nsGkAtoms::rowspacing_) {
aFrame->SetRowSpacingArray(valueList);
} else if (aAttribute == nsGkAtoms::columnspacing_) {
aFrame->SetColSpacingArray(valueList);
} else {
aFrame->SetFrameSpacing(valueList.ElementAt(0),
valueList.ElementAt(1));
}
}
static void ParseSpacingAttributes(nsMathMLmtableFrame* aTableFrame)
{
ParseSpacingAttribute(aTableFrame, nsGkAtoms::rowspacing_);
ParseSpacingAttribute(aTableFrame, nsGkAtoms::columnspacing_);
ParseSpacingAttribute(aTableFrame, nsGkAtoms::framespacing_);
aTableFrame->SetUseCSSSpacing();
}
// 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(nsMathMLmtableFrame* 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);
// Map mtable rowspacing, columnspacing & framespacing
ParseSpacingAttributes(aTableFrame);
// 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 <math> or <body> if <math> isn't there
nsIFrame* f = atLeast;
for ( ; f; f = f->GetParent()) {
nsIContent* c = f->GetContent();
if (!c || c->IsMathMLElement(nsGkAtoms::math) ||
c->NodeInfo()->NameAtom(nsGkAtoms::body)) // XXXbaku which kind of body tag?
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)
nsContainerFrame*
NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
{
}
nsresult
nsMathMLmtableOuterFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* 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->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(GetParent());
// Need to reflow the parent, not us, because this can actually
// affect siblings.
PresContext()->PresShell()->
FrameNeedsReflow(GetParent(), nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
return NS_OK;
}
// ...and the other attributes affect rows or columns in one way or another
nsPresContext* presContext = tableFrame->PresContext();
if (aAttribute == nsGkAtoms::rowspacing_ ||
aAttribute == nsGkAtoms::columnspacing_ ||
aAttribute == nsGkAtoms::framespacing_ ) {
nsMathMLmtableFrame* mathMLmtableFrame = do_QueryFrame(tableFrame);
if (mathMLmtableFrame) {
ParseSpacingAttribute(mathMLmtableFrame, aAttribute);
mathMLmtableFrame->SetUseCSSSpacing();
}
} else if (aAttribute == nsGkAtoms::rowalign_ ||
aAttribute == nsGkAtoms::rowlines_ ||
aAttribute == nsGkAtoms::columnalign_ ||
aAttribute == nsGkAtoms::columnlines_) {
// 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);
} else {
// Ignore attributes that do not affect layout.
return NS_OK;
}
// 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;
}
void
nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
nsAutoString value;
// we want to return a table that is anchored according to the align attribute
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 <mtable>
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;
WritingMode wm = aDesiredSize.GetWritingMode();
nscoord blockSize = aDesiredSize.BSize(wm);
nsIFrame* rowFrame = nullptr;
if (rowIndex) {
rowFrame = GetRowFrameAt(aPresContext, rowIndex);
if (rowFrame) {
// translate the coordinates to be relative to us and in our writing mode
nsIFrame* frame = rowFrame;
LogicalRect frameRect(wm, frame->GetRect(), aReflowState.ComputedWidth());
blockSize = frameRect.BSize(wm);
do {
dy += frameRect.BStart(wm);
frame = frame->GetParent();
} 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 ?
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
nsLayoutUtils::
FontSizeInflationFor(this));
nscoord axisHeight;
GetAxisHeight(*aReflowState.rendContext, 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);
}
}
mReference.x = 0;
mReference.y = aDesiredSize.BlockStartAscent();
// just make-up a bounding metrics
mBoundingMetrics = nsBoundingMetrics();
mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
mBoundingMetrics.descent = aDesiredSize.Height() -
aDesiredSize.BlockStartAscent();
mBoundingMetrics.width = aDesiredSize.Width();
mBoundingMetrics.leftBearing = 0;
mBoundingMetrics.rightBearing = aDesiredSize.Width();
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}
nsContainerFrame*
NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsMathMLmtableFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
nsMathMLmtableFrame::~nsMathMLmtableFrame()
{
}
void
nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList)
{
nsTableFrame::SetInitialChildList(aListID, aChildList);
MapAllAttributesIntoCSS(this);
}
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);
}
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;
}
void
nsMathMLmtableFrame::SetUseCSSSpacing()
{
mUseCSSSpacing =
!(mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::rowspacing_) ||
mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::columnspacing_) ||
mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::framespacing_));
}
NS_QUERYFRAME_HEAD(nsMathMLmtableFrame)
NS_QUERYFRAME_ENTRY(nsMathMLmtableFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsTableFrame)
// --------
// implementation of nsMathMLmtrFrame
nsContainerFrame*
NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsMathMLmtrFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
nsMathMLmtrFrame::~nsMathMLmtrFrame()
{
}
nsresult
nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
// Attributes specific to <mtr>:
// 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
nsContainerFrame*
NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
nsTableFrame* aTableFrame)
{
return new (aPresShell) nsMathMLmtdFrame(aContext, aTableFrame);
}
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
nsMathMLmtdFrame::~nsMathMLmtdFrame()
{
}
void
nsMathMLmtdFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
nsTableCellFrame::Init(aContent, aParent, aPrevInFlow);
// 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);
}
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->IsMathMLElement(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->IsMathMLElement(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;
}
nsresult
nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
// Attributes specific to <mtd>:
// 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<int8_t>* 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;
}
LogicalMargin
nsMathMLmtdFrame::GetBorderWidth(WritingMode aWM) const
{
nsStyleBorder styleBorder = *StyleBorder();
ApplyBorderToStyle(this, styleBorder);
return LogicalMargin(aWM, styleBorder.GetComputedBorder());
}
nsMargin
nsMathMLmtdFrame::GetBorderOverflow()
{
nsStyleBorder styleBorder = *StyleBorder();
ApplyBorderToStyle(this, styleBorder);
nsMargin overflow = ComputeBorderOverflow(this, styleBorder);
return overflow;
}
// --------
// implementation of nsMathMLmtdInnerFrame
NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
nsContainerFrame*
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());
}
void
nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
// Let the base class do the reflow
nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
// more about <maligngroup/> and <malignmark/> later
// ...
}
const
nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
{
// Set the default alignment in case nothing was specified
uint8_t alignment = StyleText()->mTextAlign;
nsTArray<int8_t>* 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());
}