diff --git a/mozilla/content/html/content/src/nsHTMLTableElement.cpp b/mozilla/content/html/content/src/nsHTMLTableElement.cpp index bbd8619d3b8..bcfbfc95a5d 100644 --- a/mozilla/content/html/content/src/nsHTMLTableElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLTableElement.cpp @@ -1412,39 +1412,44 @@ MapAttributesIntoRule(const nsIHTMLMappedAttributes* aAttributes, nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData); } else if (aData->mSID == eStyleStruct_Border && aData->mMarginData) { - const nsStyleDisplay* readDisplay = (nsStyleDisplay*) - aData->mStyleContext->GetStyleData(eStyleStruct_Display); - + if (!aData->mStyleContext) return; + const nsStyleTableBorder* tableStyle = + (const nsStyleTableBorder*)aData->mStyleContext->GetStyleData(eStyleStruct_TableBorder); + const nsStyleDisplay* readDisplay = + (nsStyleDisplay*) aData->mStyleContext->GetStyleData(eStyleStruct_Display); if (readDisplay && (readDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL)) { - // set the cell's border from the table - nsHTMLValue value; - aAttributes->GetAttribute(nsHTMLAtoms::border, value); - if (((value.GetUnit() == eHTMLUnit_Pixel) && - (value.GetPixelValue() > 0)) || - (value.GetUnit() == eHTMLUnit_Empty)) { - if (aData->mMarginData->mBorderWidth->mLeft.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderWidth->mLeft.SetFloatValue(1.0f, eCSSUnit_Pixel); - if (aData->mMarginData->mBorderWidth->mRight.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderWidth->mRight.SetFloatValue(1.0f, eCSSUnit_Pixel); - if (aData->mMarginData->mBorderWidth->mTop.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderWidth->mTop.SetFloatValue(1.0f, eCSSUnit_Pixel); - if (aData->mMarginData->mBorderWidth->mBottom.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderWidth->mBottom.SetFloatValue(1.0f, eCSSUnit_Pixel); + if (NS_STYLE_BORDER_SEPARATE == tableStyle->mBorderCollapse) { + // Set the cell's border from the table in the separate border model. If there is a border + // on the table, then the mapping to rules=all will take care of borders in the collapsing model. + nsHTMLValue value; + aAttributes->GetAttribute(nsHTMLAtoms::border, value); + if (((value.GetUnit() == eHTMLUnit_Pixel) && + (value.GetPixelValue() > 0)) || + (value.GetUnit() == eHTMLUnit_Empty)) { + if (aData->mMarginData->mBorderWidth->mLeft.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderWidth->mLeft.SetFloatValue(1.0f, eCSSUnit_Pixel); + if (aData->mMarginData->mBorderWidth->mRight.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderWidth->mRight.SetFloatValue(1.0f, eCSSUnit_Pixel); + if (aData->mMarginData->mBorderWidth->mTop.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderWidth->mTop.SetFloatValue(1.0f, eCSSUnit_Pixel); + if (aData->mMarginData->mBorderWidth->mBottom.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderWidth->mBottom.SetFloatValue(1.0f, eCSSUnit_Pixel); - PRUint8 borderStyle = (eCompatibility_NavQuirks == mode) - ? NS_STYLE_BORDER_STYLE_BG_INSET : NS_STYLE_BORDER_STYLE_INSET; + PRUint8 borderStyle = (eCompatibility_NavQuirks == mode) + ? NS_STYLE_BORDER_STYLE_BG_INSET : NS_STYLE_BORDER_STYLE_INSET; // BG_INSET results in a border color based on background colors // used for NavQuirks only... - if (aData->mMarginData->mBorderStyle->mLeft.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderStyle->mLeft.SetIntValue(borderStyle, eCSSUnit_Enumerated); - if (aData->mMarginData->mBorderStyle->mRight.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderStyle->mRight.SetIntValue(borderStyle, eCSSUnit_Enumerated); - if (aData->mMarginData->mBorderStyle->mTop.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderStyle->mTop.SetIntValue(borderStyle, eCSSUnit_Enumerated); - if (aData->mMarginData->mBorderStyle->mBottom.GetUnit() == eCSSUnit_Null) - aData->mMarginData->mBorderStyle->mBottom.SetIntValue(borderStyle, eCSSUnit_Enumerated); + if (aData->mMarginData->mBorderStyle->mLeft.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderStyle->mLeft.SetIntValue(borderStyle, eCSSUnit_Enumerated); + if (aData->mMarginData->mBorderStyle->mRight.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderStyle->mRight.SetIntValue(borderStyle, eCSSUnit_Enumerated); + if (aData->mMarginData->mBorderStyle->mTop.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderStyle->mTop.SetIntValue(borderStyle, eCSSUnit_Enumerated); + if (aData->mMarginData->mBorderStyle->mBottom.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderStyle->mBottom.SetIntValue(borderStyle, eCSSUnit_Enumerated); + } } } else { @@ -1458,7 +1463,6 @@ MapAttributesIntoRule(const nsIHTMLMappedAttributes* aAttributes, PRUint8 borderStyle = (eCompatibility_NavQuirks == mode) ? NS_STYLE_BORDER_STYLE_BG_OUTSET : NS_STYLE_BORDER_STYLE_OUTSET; - // bordercolor nsHTMLValue value; aAttributes->GetAttribute(nsHTMLAtoms::bordercolor, value); @@ -1476,6 +1480,18 @@ MapAttributesIntoRule(const nsIHTMLMappedAttributes* aAttributes, borderStyle = NS_STYLE_BORDER_STYLE_OUTSET; // use css outset } + else if (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse) { + // make the color grey + nscolor color = NS_RGB(80, 80, 80); + if (aData->mMarginData->mBorderColor->mLeft.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderColor->mLeft.SetColorValue(color); + if (aData->mMarginData->mBorderColor->mRight.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderColor->mRight.SetColorValue(color); + if (aData->mMarginData->mBorderColor->mTop.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderColor->mTop.SetColorValue(color); + if (aData->mMarginData->mBorderColor->mBottom.GetUnit() == eCSSUnit_Null) + aData->mMarginData->mBorderColor->mBottom.SetColorValue(color); + } // border and frame MapTableBorderInto(aAttributes, aData, borderStyle); @@ -1500,7 +1516,6 @@ nsHTMLTableElement::GetMappedAttributeImpact(const nsIAtom* aAttribute, PRInt32 (aAttribute == nsHTMLAtoms::cellpadding) || (aAttribute == nsHTMLAtoms::cellspacing) || (aAttribute == nsHTMLAtoms::cols) || - (aAttribute == nsHTMLAtoms::rules) || (aAttribute == nsHTMLAtoms::border) || (aAttribute == nsHTMLAtoms::frame) || (aAttribute == nsHTMLAtoms::width) || @@ -1512,7 +1527,10 @@ nsHTMLTableElement::GetMappedAttributeImpact(const nsIAtom* aAttribute, PRInt32 else if (aAttribute == nsHTMLAtoms::bordercolor) { aHint = NS_STYLE_HINT_VISUAL; } - else if (aAttribute == nsHTMLAtoms::align) { + else if ((aAttribute == nsHTMLAtoms::align) || + (aAttribute == nsHTMLAtoms::rules)) { + // Changing to rules will force border-collapse. Unfortunately, if border-collapse was + // already in effect, then a frame change is not necessary. aHint = NS_STYLE_HINT_FRAMECHANGE; } else if (!GetCommonMappedAttributesImpact(aAttribute, aHint)) { diff --git a/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp b/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp index 2421da50e03..1ec64eabe97 100644 --- a/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp @@ -475,6 +475,254 @@ TableTHRule::MapRuleInfoInto(nsRuleData* aRuleData) return NS_OK; } +static void +ProcessTableRulesAttribute(nsStyleStruct* aStyleStruct, + nsRuleData* aRuleData, + PRUint8 aSide, + PRBool aGroup, + PRUint8 aRulesArg1, + PRUint8 aRulesArg2, + PRUint8 aRulesArg3) +{ + if (!aStyleStruct || !aRuleData || !aRuleData->mPresContext) return; + + nsCOMPtr tableContext = getter_AddRefs(aRuleData->mStyleContext->GetParent()); if (!tableContext) return; + if (!aGroup) { + tableContext = getter_AddRefs(tableContext->GetParent()); if (!tableContext) return; + } + + const nsStyleTable* tableData = + (const nsStyleTable*)tableContext->GetStyleData(eStyleStruct_Table); + if (tableData && ((aRulesArg1 == tableData->mRules) || + (aRulesArg2 == tableData->mRules) || + (aRulesArg3 == tableData->mRules))) { + const nsStyleBorder* tableBorderData = + (const nsStyleBorder*)tableContext->GetStyleData(eStyleStruct_Border); if (!tableBorderData) return; + PRUint8 tableBorderStyle = tableBorderData->GetBorderStyle(aSide); + + nsStyleBorder* borderData = (nsStyleBorder*)aStyleStruct; if (!borderData) return; + PRUint8 borderStyle = borderData->GetBorderStyle(aSide); + // XXX It appears that the style system erronously applies the custom style rule after css style, + // consequently it does not properly fit into the casade. For now, assume that a border style of none + // implies that the style has not been set. + if (NS_STYLE_BORDER_STYLE_NONE == borderStyle) { + // use the table's border style if it is dashed or dotted, otherwise use solid + PRUint8 bStyle = ((NS_STYLE_BORDER_STYLE_NONE != tableBorderStyle) && + (NS_STYLE_BORDER_STYLE_HIDDEN != tableBorderStyle)) + ? tableBorderStyle : NS_STYLE_BORDER_STYLE_SOLID; + if ((NS_STYLE_BORDER_STYLE_DASHED != bStyle) && + (NS_STYLE_BORDER_STYLE_DOTTED != bStyle) && + (NS_STYLE_BORDER_STYLE_SOLID != bStyle)) { + bStyle = NS_STYLE_BORDER_STYLE_SOLID; + } + bStyle |= NS_STYLE_BORDER_STYLE_RULES_MASK; + borderData->SetBorderStyle(aSide, bStyle); + + nscolor borderColor; + PRBool transparent, foreground; + borderData->GetBorderColor(aSide, borderColor, transparent, foreground); + if (transparent || foreground) { + // use the table's border color if it is set, otherwise use black + nscolor tableBorderColor; + tableBorderData->GetBorderColor(aSide, tableBorderColor, transparent, foreground); + borderColor = (transparent || foreground) ? NS_RGB(0,0,0) : tableBorderColor; + borderData->SetBorderColor(aSide, borderColor); + } + // set the border width to be 1 pixel + float p2t; + aRuleData->mPresContext->GetScaledPixelsToTwips(&p2t); + nscoord onePixel = NSToCoordRound(p2t); + nsStyleCoord coord(onePixel); + switch(aSide) { + case NS_SIDE_TOP: + borderData->mBorder.SetTop(coord); + break; + case NS_SIDE_RIGHT: + borderData->mBorder.SetRight(coord); + break; + case NS_SIDE_BOTTOM: + borderData->mBorder.SetBottom(coord); + break; + default: // NS_SIDE_LEFT + borderData->mBorder.SetLeft(coord); + break; + } + } + } +} + +// ----------------------------------------------------------- +// this rule handles borders on a , , when rules is set on its +// ----------------------------------------------------------- +class TableTbodyRule: public GenericTableRule { +public: + TableTbodyRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableTbodyRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableTbodyRule::TableTbodyRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableTbodyRule::~TableTbodyRule() +{ +} + +static void TbodyPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_TOP, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_ROWS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_BOTTOM, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_ROWS); +} + +NS_IMETHODIMP +TableTbodyRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &TbodyPostResolveCallback; + } + return NS_OK; +} +// ----------------------------------------------------------- + +// ----------------------------------------------------------- +// this rule handles borders on a when rules is set on its
+// ----------------------------------------------------------- +class TableRowRule: public GenericTableRule { +public: + TableRowRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableRowRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableRowRule::TableRowRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableRowRule::~TableRowRule() +{ +} + +static void RowPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_TOP, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_ROWS, NS_STYLE_TABLE_RULES_ROWS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_BOTTOM, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_ROWS, NS_STYLE_TABLE_RULES_ROWS); +} + +NS_IMETHODIMP +TableRowRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &RowPostResolveCallback; + } + return NS_OK; +} + +// ----------------------------------------------------------- +// this rule handles borders on a when rules is set on its
+// ----------------------------------------------------------- +class TableColgroupRule: public GenericTableRule { +public: + TableColgroupRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableColgroupRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableColgroupRule::TableColgroupRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableColgroupRule::~TableColgroupRule() +{ +} + +static void ColgroupPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_LEFT, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_COLS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_RIGHT, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_COLS); +} + +NS_IMETHODIMP +TableColgroupRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &ColgroupPostResolveCallback; + } + return NS_OK; +} + +// ----------------------------------------------------------- +// this rule handles borders on a when rules is set on its
+// ----------------------------------------------------------- +class TableColRule: public GenericTableRule { +public: + TableColRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableColRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableColRule::TableColRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableColRule::~TableColRule() +{ +} + +static void ColPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_LEFT, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_COLS, NS_STYLE_TABLE_RULES_COLS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_RIGHT, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_COLS, NS_STYLE_TABLE_RULES_COLS); +} + +NS_IMETHODIMP +TableColRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &ColPostResolveCallback; + } + return NS_OK; +} // ----------------------------------------------------------- class AttributeKey: public nsHashKey @@ -653,6 +901,10 @@ protected: HTMLColorRule* mVisitedRule; HTMLColorRule* mActiveRule; HTMLDocumentColorRule* mDocumentColorRule; + TableTbodyRule* mTableTbodyRule; + TableRowRule* mTableRowRule; + TableColgroupRule* mTableColgroupRule; + TableColRule* mTableColRule; TableTHRule* mTableTHRule; // NOTE: if adding more rules, be sure to update // the SizeOf method to include them @@ -710,6 +962,26 @@ HTMLStyleSheetImpl::HTMLStyleSheetImpl(void) nsresult HTMLStyleSheetImpl::Init() { + mTableTbodyRule = new TableTbodyRule(this); + if (!mTableTbodyRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableTbodyRule); + + mTableRowRule = new TableRowRule(this); + if (!mTableRowRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableRowRule); + + mTableColgroupRule = new TableColgroupRule(this); + if (!mTableColgroupRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableColgroupRule); + + mTableColRule = new TableColRule(this); + if (!mTableColRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableColRule); + mTableTHRule = new TableTHRule(this); if (!mTableTHRule) return NS_ERROR_OUT_OF_MEMORY; @@ -750,6 +1022,22 @@ HTMLStyleSheetImpl::~HTMLStyleSheetImpl() mDocumentColorRule->mSheet = nsnull; NS_RELEASE(mDocumentColorRule); } + if (nsnull != mTableTbodyRule) { + mTableTbodyRule->mSheet = nsnull; + NS_RELEASE(mTableTbodyRule); + } + if (nsnull != mTableRowRule) { + mTableRowRule->mSheet = nsnull; + NS_RELEASE(mTableRowRule); + } + if (nsnull != mTableColgroupRule) { + mTableColgroupRule->mSheet = nsnull; + NS_RELEASE(mTableColgroupRule); + } + if (nsnull != mTableColRule) { + mTableColRule->mSheet = nsnull; + NS_RELEASE(mTableColRule); + } if (nsnull != mTableTHRule) { mTableTHRule->mSheet = nsnull; NS_RELEASE(mTableTHRule); @@ -848,6 +1136,18 @@ HTMLStyleSheetImpl::RulesMatching(ElementRuleProcessorData* aData, else if (tag == nsHTMLAtoms::th) { ruleWalker->Forward(mTableTHRule); } + else if (tag == nsHTMLAtoms::tr) { + ruleWalker->Forward(mTableRowRule); + } + else if ((tag == nsHTMLAtoms::thead) || (tag == nsHTMLAtoms::tbody) || (tag == nsHTMLAtoms::tfoot)) { + ruleWalker->Forward(mTableTbodyRule); + } + else if (tag == nsHTMLAtoms::col) { + ruleWalker->Forward(mTableColRule); + } + else if (tag == nsHTMLAtoms::colgroup) { + ruleWalker->Forward(mTableColgroupRule); + } else if (tag == nsHTMLAtoms::table) { if (aData->mIsQuirkMode) ruleWalker->Forward(mDocumentColorRule); @@ -888,7 +1188,13 @@ NS_IMETHODIMP HTMLStyleSheetImpl::RulesMatching(PseudoRuleProcessorData* aData, nsIAtom* aMedium) { - // no pseudo frame style + nsIAtom* pseudoTag = aData->mPseudoTag; + if (pseudoTag == nsHTMLAtoms::tableColPseudo) { + nsRuleWalker *ruleWalker = aData->mRuleWalker; + if (ruleWalker) { + ruleWalker->Forward(mTableColRule); + } + } return NS_OK; } @@ -1008,6 +1314,11 @@ HTMLStyleSheetImpl::Reset(nsIURI* aURL) NS_RELEASE(mActiveRule); } mDocumentColorRule->Reset(); + + mTableTbodyRule->Reset(); + mTableRowRule->Reset(); + mTableColgroupRule->Reset(); + mTableColRule->Reset(); mTableTHRule->Reset(); mMappedAttrTable.Enumerate(MappedDropSheet); @@ -1264,6 +1575,10 @@ MappedSizeAttributes(nsHashKey *aKey, void *aData, void* closure) * - mVisitedRule * - mActiveRule * - mDocumentColorRule +* - mTableTbodyRule +* - mTableRowRule +* - mTableColgroupRule +* - mTableColRule * - mTableTHRule * - mMappedAttrTable * 2) Delegates (really) to the MappedAttributes in the mMappedAttrTable @@ -1298,6 +1613,10 @@ HTMLStyleSheetImpl::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) // - mVisitedRule : sizeof object // - mActiveRule : sizeof object // - mDocumentColorRule : sizeof object + // - mTableTbodyRule : sizeof object + // - mTableRowRule : sizeof object + // - mTableColgroupRule : sizeof object + // - mTableColRule : sizeof object // - mTableTHRule : sizeof object // - mMappedAttrTable @@ -1321,6 +1640,26 @@ HTMLStyleSheetImpl::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) tag = getter_AddRefs(NS_NewAtom("DocumentColorRule")); aSizeOfHandler->AddSize(tag,localSize); } + if(uniqueItems->AddItem((void*)mTableTbodyRule)){ + localSize = sizeof(*mTableTbodyRule); + tag = getter_AddRefs(NS_NewAtom("TableTbodyRule")); + aSizeOfHandler->AddSize(tag,localSize); + } + if(uniqueItems->AddItem((void*)mTableRowRule)){ + localSize = sizeof(*mTableRowRule); + tag = getter_AddRefs(NS_NewAtom("TableRowRule")); + aSizeOfHandler->AddSize(tag,localSize); + } + if(uniqueItems->AddItem((void*)mTableColgroupRule)){ + localSize = sizeof(*mTableColgroupRule); + tag = getter_AddRefs(NS_NewAtom("TableColgroupRule")); + aSizeOfHandler->AddSize(tag,localSize); + } + if(uniqueItems->AddItem((void*)mTableColRule)){ + localSize = sizeof(*mTableColRule); + tag = getter_AddRefs(NS_NewAtom("TableColRule")); + aSizeOfHandler->AddSize(tag,localSize); + } if(uniqueItems->AddItem((void*)mTableTHRule)){ localSize = sizeof(*mTableTHRule); tag = getter_AddRefs(NS_NewAtom("TableTHRule")); diff --git a/mozilla/content/shared/public/nsCSSPropList.h b/mozilla/content/shared/public/nsCSSPropList.h index ce14a5df95e..3007f42b36f 100644 --- a/mozilla/content/shared/public/nsCSSPropList.h +++ b/mozilla/content/shared/public/nsCSSPropList.h @@ -103,7 +103,7 @@ CSS_PROP(border-bottom-color, border_bottom_color, VISUAL) CSS_PROP(-moz-border-bottom-colors, border_bottom_colors, VISUAL) CSS_PROP(border-bottom-style, border_bottom_style, REFLOW) // on/off will need reflow CSS_PROP(border-bottom-width, border_bottom_width, REFLOW) -CSS_PROP(border-collapse, border_collapse, REFLOW) +CSS_PROP(border-collapse, border_collapse, FRAMECHANGE) CSS_PROP(border-color, border_color, VISUAL) CSS_PROP(border-left, border_left, REFLOW) CSS_PROP(border-left-color, border_left_color, VISUAL) diff --git a/mozilla/content/shared/public/nsLayoutAtomList.h b/mozilla/content/shared/public/nsLayoutAtomList.h index 8a8cfd14e5e..5cb1b488129 100644 --- a/mozilla/content/shared/public/nsLayoutAtomList.h +++ b/mozilla/content/shared/public/nsLayoutAtomList.h @@ -100,6 +100,7 @@ LAYOUT_ATOM(selectScrolledContentPseudo, ":-moz-select-scrolled-content") // Alphabetical list of frame types LAYOUT_ATOM(areaFrame, "AreaFrame") +LAYOUT_ATOM(bcTableCellFrame, "BCTableCellFrame") // table cell in border collapsing model LAYOUT_ATOM(blockFrame, "BlockFrame") LAYOUT_ATOM(boxFrame, "BoxFrame") LAYOUT_ATOM(brFrame, "BRFrame") @@ -143,7 +144,8 @@ LAYOUT_ATOM(overflowProperty, "OverflowProperty") // list of nsIFra LAYOUT_ATOM(overflowLinesProperty, "OverflowLinesProperty") // list of nsLineBox* LAYOUT_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty") // nscoord* LAYOUT_ATOM(spaceManagerProperty, "SpaceManagerProperty") // the space manager for a block -LAYOUT_ATOM(viewProperty, "ViewProperty") // nsView* +LAYOUT_ATOM(tableBCProperty, "TableBCProperty") // table border collapsing info (e.g. damage area, table border widths) +LAYOUT_ATOM(viewProperty, "ViewProperty") // Alphabetical list of event handler names LAYOUT_ATOM(onabort, "onabort") diff --git a/mozilla/content/shared/public/nsStyleStruct.h b/mozilla/content/shared/public/nsStyleStruct.h index 9627f3804b8..2303efea81c 100644 --- a/mozilla/content/shared/public/nsStyleStruct.h +++ b/mozilla/content/shared/public/nsStyleStruct.h @@ -465,7 +465,8 @@ struct nsStyleBorder: public nsStyleStruct { // XXX these are deprecated methods void CalcBorderFor(const nsIFrame* aFrame, nsMargin& aBorder) const; - + void CalcBorderFor(const nsIFrame* aFrame, PRUint8 aSide, nscoord& aWidth) const; + protected: PRPackedBool mHasCachedBorder; nsMargin mCachedBorder; diff --git a/mozilla/content/shared/src/nsStyleStruct.cpp b/mozilla/content/shared/src/nsStyleStruct.cpp index 79bf6e4e9a9..4efb4816291 100644 --- a/mozilla/content/shared/src/nsStyleStruct.cpp +++ b/mozilla/content/shared/src/nsStyleStruct.cpp @@ -605,6 +605,28 @@ nsStyleBorder::CalcBorderFor(const nsIFrame* aFrame, nsMargin& aBorder) const } } +void +nsStyleBorder::CalcBorderFor(const nsIFrame* aFrame, PRUint8 aSide, nscoord& aWidth) const +{ + aWidth = 0; + // using mCachedBorder as above, doesn't work properly + nsStyleCoord coord; + switch(aSide) { + case NS_SIDE_TOP: + coord = mBorder.GetTop(coord); + break; + case NS_SIDE_RIGHT: + coord = mBorder.GetRight(coord); + break; + case NS_SIDE_BOTTOM: + coord = mBorder.GetBottom(coord); + break; + default: // NS_SIDE_LEFT + coord = mBorder.GetLeft(coord); + } + aWidth = CalcSideFor(aFrame, coord, NS_SPACING_BORDER, aSide, mBorderWidths, 3); +} + nsStyleOutline::nsStyleOutline(nsIPresContext* aPresContext) { // XXX support mBorderWidths until deprecated methods are removed @@ -862,7 +884,7 @@ nsStyleTable::nsStyleTable() mLayoutStrategy = NS_STYLE_TABLE_LAYOUT_AUTO; mCols = NS_STYLE_TABLE_COLS_NONE; mFrame = NS_STYLE_TABLE_FRAME_NONE; - mRules = NS_STYLE_TABLE_RULES_ALL; + mRules = NS_STYLE_TABLE_RULES_NONE; mSpan = 1; } diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index e20226d01b6..113297119be 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -52,8 +52,11 @@ #include "nsIPresContext.h" #include "nsILinkHandler.h" #include "nsIDocument.h" +#include "nsTableFrame.h" #include "nsTableColGroupFrame.h" #include "nsTableColFrame.h" +#include "nsIDOMHTMLTableColElement.h" +#include "nsTableCellFrame.h" // to get IS_CELL_FRAME #include "nsHTMLIIDs.h" #include "nsIStyleFrameConstruction.h" #include "nsHTMLParts.h" @@ -859,6 +862,23 @@ nsFrameConstructorSaveState::~nsFrameConstructorSaveState() } } + +static +PRBool IsBorderCollapse(nsIFrame* aFrame) +{ + nsIFrame* frame = aFrame; + while (frame) { + nsCOMPtr fType; + frame->GetFrameType(getter_AddRefs(fType)); + if (nsLayoutAtoms::tableFrame == fType.get()) { + return ((nsTableFrame*)frame)->IsBorderCollapse(); + } + frame->GetParent(&frame); + } + NS_ASSERTION(PR_FALSE, "program error"); + return PR_FALSE; +} + // ----------------------------------------------------------- // Structure used when creating table frames. @@ -870,7 +890,7 @@ struct nsTableCreator { virtual nsresult CreateTableColFrame(nsIFrame** aNewFrame); virtual nsresult CreateTableColGroupFrame(nsIFrame** aNewFrame); virtual nsresult CreateTableRowFrame(nsIFrame** aNewFrame); - virtual nsresult CreateTableCellFrame(nsIFrame** aNewFrame); + virtual nsresult CreateTableCellFrame(nsIFrame* aParentFrame, nsIFrame** aNewFrame); virtual nsresult CreateTableCellInnerFrame(nsIFrame** aNewFrame); nsTableCreator(nsIPresShell* aPresShell) @@ -919,8 +939,9 @@ nsTableCreator::CreateTableRowFrame(nsIFrame** aNewFrame) { } nsresult -nsTableCreator::CreateTableCellFrame(nsIFrame** aNewFrame) { - return NS_NewTableCellFrame(mPresShell, aNewFrame); +nsTableCreator::CreateTableCellFrame(nsIFrame* aParentFrame, + nsIFrame** aNewFrame) { + return NS_NewTableCellFrame(mPresShell, IsBorderCollapse(aParentFrame), aNewFrame); } nsresult @@ -1527,7 +1548,7 @@ IsTableRelated(nsIAtom* aParentType, ((nsLayoutAtoms::tableCaptionFrame == aParentType) || (nsLayoutAtoms::tableColGroupFrame == aParentType) || (nsLayoutAtoms::tableColFrame == aParentType) || - (nsLayoutAtoms::tableCellFrame == aParentType))) { + IS_TABLE_CELL(aParentType))) { return PR_TRUE; } else return PR_FALSE; @@ -1629,7 +1650,7 @@ ProcessPseudoFrames(nsIPresContext* aPresContext, if (aPseudoFrames.mCellOuter.mFrame) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; } if (aPseudoFrames.mRow.mFrame) { rv = ProcessPseudoFrame(aPresContext, aPseudoFrames.mRow, aHighestFrame); @@ -1650,7 +1671,7 @@ ProcessPseudoFrames(nsIPresContext* aPresContext, } if (aPseudoFrames.mCellOuter.mFrame) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; } if (aPseudoFrames.mRow.mFrame) { rv = ProcessPseudoFrame(aPresContext, aPseudoFrames.mRow, aHighestFrame); @@ -1671,12 +1692,12 @@ ProcessPseudoFrames(nsIPresContext* aPresContext, } if (aPseudoFrames.mCellOuter.mFrame) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; } } - else if (nsLayoutAtoms::tableCellFrame == aPseudoFrames.mLowestType) { + else if (IS_TABLE_CELL(aPseudoFrames.mLowestType)) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; if (aPseudoFrames.mRow.mFrame) { rv = ProcessPseudoFrame(aPresContext, aPseudoFrames.mRow, aHighestFrame); @@ -1942,6 +1963,7 @@ nsCSSFrameConstructor::CreatePseudoCellFrame(nsIPresShell* aPresShell // set pseudo data for the newly created frames pseudoOuter.mChildList.AddChild(pseudoInner.mFrame); + // give it nsLayoutAtoms::tableCellFrame, if it is really nsLayoutAtoms::bcTableCellFrame, it will match later aState.mPseudoFrames.mLowestType = nsLayoutAtoms::tableCellFrame; // set pseudo data for the parent @@ -2021,7 +2043,7 @@ nsCSSFrameConstructor::GetPseudoColGroupFrame(nsIPresShell* aPresShel rv = CreatePseudoCellFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); created = PR_TRUE; } - if (created || (nsLayoutAtoms::tableCellFrame == parentFrameType.get()) || // cell parent + if (created || IS_TABLE_CELL(parentFrameType.get()) || // cell parent !IsTableRelated(parentFrameType.get(), PR_TRUE)) { // block parent rv = CreatePseudoTableFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); } @@ -2065,7 +2087,7 @@ nsCSSFrameConstructor::GetPseudoRowGroupFrame(nsIPresShell* aPresShel rv = CreatePseudoCellFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); created = PR_TRUE; } - if (created || (nsLayoutAtoms::tableCellFrame == parentFrameType.get()) || // cell parent + if (created || IS_TABLE_CELL(parentFrameType.get()) || // cell parent !IsTableRelated(parentFrameType.get(), PR_TRUE)) { // block parent rv = CreatePseudoTableFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); } @@ -2102,7 +2124,7 @@ nsCSSFrameConstructor::GetPseudoRowFrame(nsIPresShell* aPresShell, if (pseudoFrames.IsEmpty()) { PRBool created = PR_FALSE; - if ((nsLayoutAtoms::tableCellFrame == parentFrameType.get()) || // cell parent + if (IS_TABLE_CELL(parentFrameType.get()) || // cell parent !IsTableRelated(parentFrameType.get(), PR_TRUE)) { // block parent rv = CreatePseudoTableFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); created = PR_TRUE; @@ -2230,7 +2252,7 @@ nsCSSFrameConstructor::GetParentFrame(nsIPresShell* aPresShell, pseudoParentFrame = pseudoFrames.mRowGroup.mFrame; } } - else if (nsLayoutAtoms::tableCellFrame == aChildFrameType) { // cell child + else if (IS_TABLE_CELL(aChildFrameType)) { // cell child if (nsLayoutAtoms::tableRowFrame != parentFrameType.get()) { // need pseudo row parent rv = GetPseudoRowFrame(aPresShell, aPresContext, aTableCreator, aState, aParentFrameIn); if (NS_FAILED(rv)) return rv; @@ -2572,7 +2594,7 @@ nsCSSFrameConstructor::ConstructTableColFrame(nsIPresShell* aPresShel PRBool& aIsPseudoParent) { nsresult rv = NS_OK; - if (!aPresShell || !aPresContext || !aParentFrameIn) return rv; + if (!aPresShell || !aPresContext || !aParentFrameIn || !aStyleContext) return rv; nsIFrame* parentFrame = aParentFrameIn; aIsPseudoParent = PR_FALSE; @@ -2585,16 +2607,42 @@ nsCSSFrameConstructor::ConstructTableColFrame(nsIPresShell* aPresShel } } - rv = aTableCreator.CreateTableColFrame(&aNewFrame); - if (NS_FAILED(rv)) return rv; - InitAndRestoreFrame(aPresContext, aState, aContent, - parentFrame, aStyleContext, nsnull, aNewFrame); + rv = aTableCreator.CreateTableColFrame(&aNewFrame); if (NS_FAILED(rv)) return rv; + InitAndRestoreFrame(aPresContext, aState, aContent, parentFrame, aStyleContext, nsnull, aNewFrame); + // if the parent frame was anonymous then reparent the style context + nsCOMPtr parentStyleContext; + parentFrame->GetStyleContext(getter_AddRefs(parentStyleContext)); + if (aIsPseudoParent) { + aPresContext->ReParentStyleContext(aNewFrame, parentStyleContext); + } + + // construct additional col frames if the col frame has a span > 1 + PRInt32 span = 1; + nsCOMPtr cgContent(do_QueryInterface(aContent)); + if (cgContent) { + cgContent->GetSpan(&span); + nsIFrame* lastCol = aNewFrame; + for (PRInt32 spanX = 1; spanX < span; spanX++) { + nsCOMPtr styleContext; + aPresContext->ResolvePseudoStyleContextFor(aContent, nsHTMLAtoms::tableColPseudo, aStyleContext, + PR_FALSE, getter_AddRefs(styleContext)); + nsIFrame* newCol; + rv = aTableCreator.CreateTableColFrame(&newCol); if (NS_FAILED(rv)) return rv; + InitAndRestoreFrame(aPresContext, aState, aContent, parentFrame, styleContext, nsnull, newCol); + if (aIsPseudoParent) { + aPresContext->ReParentStyleContext(newCol, aStyleContext); + } + ((nsTableColFrame*)newCol)->SetType(eColAnonymousCol); + lastCol->SetNextSibling(newCol); + lastCol = newCol; + } + } + if (!aIsPseudo) { nsFrameItems childItems; nsIFrame* captionFrame; rv = TableProcessChildren(aPresShell, aPresContext, aState, aContent, aNewFrame, - aTableCreator, childItems, captionFrame); - if (NS_FAILED(rv)) return rv; + aTableCreator, childItems, captionFrame); if (NS_FAILED(rv)) return rv; aNewFrame->SetInitialChildList(aPresContext, nsnull, childItems.childList); if (aIsPseudoParent) { aState.mPseudoFrames.mColGroup.mChildList.AddChild(aNewFrame); @@ -2625,6 +2673,7 @@ nsCSSFrameConstructor::ConstructTableCellFrame(nsIPresShell* aPresShe aIsPseudoParent = PR_FALSE; if (!aIsPseudo) { // this frame may have a pseudo parent + // use nsLayoutAtoms::tableCellFrame which will match if it is really nsLayoutAtoms::bcTableCellFrame GetParentFrame(aPresShell, aPresContext, aTableCreator, *aParentFrameIn, nsLayoutAtoms::tableCellFrame, aState, parentFrame, aIsPseudoParent); if (!aIsPseudoParent && !aState.mPseudoFrames.IsEmpty()) { @@ -2635,7 +2684,7 @@ nsCSSFrameConstructor::ConstructTableCellFrame(nsIPresShell* aPresShe } } - rv = aTableCreator.CreateTableCellFrame(&aNewCellOuterFrame); + rv = aTableCreator.CreateTableCellFrame(parentFrame, &aNewCellOuterFrame); if (NS_FAILED(rv)) return rv; // Initialize the table cell frame @@ -11328,7 +11377,7 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsIPresShell* aPresShell, // See if it's a table cell frame cellFrame->GetFrameType(&tableType); - if (nsLayoutAtoms::tableCellFrame == tableType) { + if (IS_TABLE_CELL(tableType)) { nsIFrame* continuingCellFrame; CreateContinuingFrame(aPresShell, aPresContext, cellFrame, newFrame, &continuingCellFrame); @@ -11343,8 +11392,8 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsIPresShell* aPresShell, newFrame->SetInitialChildList(aPresContext, nsnull, newChildList.childList); } - } else if (nsLayoutAtoms::tableCellFrame == frameType) { - rv = NS_NewTableCellFrame(aPresShell, &newFrame); + } else if (IS_TABLE_CELL(frameType)) { + rv = NS_NewTableCellFrame(aPresShell, IsBorderCollapse(aParentFrame), &newFrame); if (NS_SUCCEEDED(rv)) { newFrame->Init(aPresContext, content, aParentFrame, styleContext, aFrame); nsHTMLContainerFrame::CreateViewForFrame(aPresContext, newFrame, diff --git a/mozilla/layout/base/nsCSSRendering.cpp b/mozilla/layout/base/nsCSSRendering.cpp index 543a729ae1f..ea8f7cdea80 100644 --- a/mozilla/layout/base/nsCSSRendering.cpp +++ b/mozilla/layout/base/nsCSSRendering.cpp @@ -514,6 +514,7 @@ void nsCSSRendering::DrawSide(nsIRenderingContext& aContext, } } + /** * Draw a dotted/dashed sides of a box */ @@ -3821,3 +3822,356 @@ void FillOrInvertRect(nsIRenderingContext& aRC, const nsRect& aRect, PRBool aInv } } +// Begin table border-collapsing section +// These functions were written to not disrupt the normal ones and yet satisfy some additional requirements +// At some point, all functions should be unified to include the additional functionality that these provide + +static nscoord +RoundIntToPixel(nscoord aValue, + nscoord aTwipsPerPixel, + PRBool aRoundDown = PR_FALSE) +{ + nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f); + nscoord extra = aValue % aTwipsPerPixel; + nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra; + + return finalValue; +} + +static nscoord +RoundFloatToPixel(float aValue, + nscoord aTwipsPerPixel, + PRBool aRoundDown = PR_FALSE) +{ + return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown); +} + +static void +SetPoly(const nsRect& aRect, + nsPoint* poly) +{ + poly[0].x = aRect.x; + poly[0].y = aRect.y; + poly[1].x = aRect.x + aRect.width; + poly[1].y = aRect.y; + poly[2].x = aRect.x + aRect.width; + poly[2].y = aRect.y + aRect.height; + poly[3].x = aRect.x; + poly[3].y = aRect.y + aRect.height; + poly[4].x = aRect.x; + poly[4].y = aRect.y; +} + +static void +DrawSolidBorderSegment(nsIRenderingContext& aContext, + nsRect aRect, + nscoord aTwipsPerPixel, + PRUint8 aStartBevelSide = 0, + nscoord aStartBevelOffset = 0, + PRUint8 aEndBevelSide = 0, + nscoord aEndBevelOffset = 0) +{ + + if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) || + ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) { + // simple line or rectangle + if ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)) { + if (1 == aRect.height) + aContext.DrawLine(aRect.x, aRect.y, aRect.x, aRect.y + aRect.height); + else + aContext.FillRect(aRect); + } + else { + if (1 == aRect.width) + aContext.DrawLine(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y); + else + aContext.FillRect(aRect); + } + } + else { + // polygon with beveling + nsPoint poly[5]; + SetPoly(aRect, poly); + switch(aStartBevelSide) { + case NS_SIDE_TOP: + poly[0].x += aStartBevelOffset; + poly[4].x = poly[0].x; + break; + case NS_SIDE_BOTTOM: + poly[3].x += aStartBevelOffset; + break; + case NS_SIDE_RIGHT: + poly[1].y += aStartBevelOffset; + break; + case NS_SIDE_LEFT: + poly[0].y += aStartBevelOffset; + poly[4].y = poly[0].y; + } + + switch(aEndBevelSide) { + case NS_SIDE_TOP: + poly[1].x -= aEndBevelOffset; + break; + case NS_SIDE_BOTTOM: + poly[2].x -= aEndBevelOffset; + break; + case NS_SIDE_RIGHT: + poly[2].y -= aEndBevelOffset; + break; + case NS_SIDE_LEFT: + poly[3].y -= aEndBevelOffset; + } + + aContext.FillPolygon(poly, 5); + } + + +} + +static void +GetDashInfo(nscoord aBorderLength, + nscoord aDashLength, + nscoord aTwipsPerPixel, + PRInt32& aNumDashSpaces, + nscoord& aStartDashLength, + nscoord& aEndDashLength) +{ + aNumDashSpaces = 0; + if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) { + aStartDashLength = aBorderLength; + aEndDashLength = 0; + } + else { + aNumDashSpaces = aBorderLength / (2 * aDashLength); // round down + nscoord foo = ((2 * aNumDashSpaces) - 1) * aDashLength; + nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength); + if (extra > 0) { + nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel); + aStartDashLength += half; + aEndDashLength += (extra - half); + } + } +} + +void +nsCSSRendering::DrawTableBorderSegment(nsIRenderingContext& aContext, + PRUint8 aBorderStyle, + nscolor aBorderColor, + const nsStyleBackground* aBGColor, + const nsRect& aBorder, + float aPixelsToTwips, + PRUint8 aStartBevelSide, + nscoord aStartBevelOffset, + PRUint8 aEndBevelSide, + nscoord aEndBevelOffset) +{ + aContext.SetColor (aBorderColor); + + PRBool horizontal = ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)); + nscoord twipsPerPixel = NSIntPixelsToTwips(1, aPixelsToTwips); + PRBool ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE; + + if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) || + (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) { + // no beveling for 1 pixel border, dash or dot + aStartBevelOffset = 0; + aEndBevelOffset = 0; + } + + switch (aBorderStyle) { + case NS_STYLE_BORDER_STYLE_NONE: + case NS_STYLE_BORDER_STYLE_HIDDEN: + case NS_STYLE_BORDER_STYLE_BLANK: + //NS_ASSERTION(PR_FALSE, "style of none, hidden, or blank"); + break; + case NS_STYLE_BORDER_STYLE_DOTTED: + case NS_STYLE_BORDER_STYLE_DASHED: + { + nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH; + // make the dash length proportional to the border thickness + dashLength *= (horizontal) ? aBorder.height : aBorder.width; + // make the min dash length for the ends 1/2 the dash length + nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) + ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength; + minDashLength = PR_MAX(minDashLength, twipsPerPixel); + nscoord numDashSpaces = 0; + nscoord startDashLength = minDashLength; + nscoord endDashLength = minDashLength; + if (horizontal) { + GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength); + nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height); + DrawSolidBorderSegment(aContext, rect, PR_TRUE); + for (PRInt32 spaceX = 0; spaceX < numDashSpaces; spaceX++) { + rect.x += rect.width + dashLength; + rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength; + DrawSolidBorderSegment(aContext, rect, PR_TRUE); + } + } + else { + GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength); + nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength); + DrawSolidBorderSegment(aContext, rect, PR_FALSE); + for (PRInt32 spaceY = 0; spaceY < numDashSpaces; spaceY++) { + rect.y += rect.height + dashLength; + rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength; + DrawSolidBorderSegment(aContext, rect, PR_FALSE); + } + } + } + break; + case NS_STYLE_BORDER_STYLE_GROOVE: + ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge + case NS_STYLE_BORDER_STYLE_RIDGE: + if ((horizontal && (twipsPerPixel >= aBorder.height)) || + (!horizontal && (twipsPerPixel >= aBorder.width))) { + // a one pixel border + DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, aStartBevelOffset, + aEndBevelSide, aEndBevelOffset); + } + else { + nscoord startBevel = (aStartBevelOffset > 0) + ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, PR_TRUE) : 0; + nscoord endBevel = (aEndBevelOffset > 0) + ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, PR_TRUE) : 0; + PRUint8 ridgeGrooveSide = (horizontal) ? NS_SIDE_TOP : NS_SIDE_LEFT; + aContext.SetColor ( + MakeBevelColor (ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor, PR_TRUE)); + nsRect rect(aBorder); + nscoord half; + if (horizontal) { // top, bottom + half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel); + rect.height = half; + if (NS_SIDE_TOP == aStartBevelSide) { + rect.x += startBevel; + rect.width -= startBevel; + } + if (NS_SIDE_TOP == aEndBevelSide) { + rect.width -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + else { // left, right + half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel); + rect.width = half; + if (NS_SIDE_LEFT == aStartBevelSide) { + rect.y += startBevel; + rect.height -= startBevel; + } + if (NS_SIDE_LEFT == aEndBevelSide) { + rect.height -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + + rect = aBorder; + ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT; + aContext.SetColor ( + MakeBevelColor (ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor, PR_TRUE)); + if (horizontal) { + rect.y = rect.y + half; + rect.height = aBorder.height - half; + if (NS_SIDE_BOTTOM == aStartBevelSide) { + rect.x += startBevel; + rect.width -= startBevel; + } + if (NS_SIDE_BOTTOM == aEndBevelSide) { + rect.width -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + else { + rect.x = rect.x + half; + rect.width = aBorder.width - half; + if (NS_SIDE_RIGHT == aStartBevelSide) { + rect.y += aStartBevelOffset - startBevel; + rect.height -= startBevel; + } + if (NS_SIDE_RIGHT == aEndBevelSide) { + rect.height -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + } + break; + case NS_STYLE_BORDER_STYLE_DOUBLE: + if ((aBorder.width > 2) && (aBorder.height > 2)) { + nscoord startBevel = (aStartBevelOffset > 0) + ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0; + nscoord endBevel = (aEndBevelOffset > 0) + ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0; + if (horizontal) { // top, bottom + nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel); + + // draw the top line or rect + nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight); + if (NS_SIDE_TOP == aStartBevelSide) { + topRect.x += aStartBevelOffset - startBevel; + topRect.width -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_TOP == aEndBevelSide) { + topRect.width -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, topRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + + // draw the botom line or rect + nscoord heightOffset = aBorder.height - thirdHeight; + nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset); + if (NS_SIDE_BOTTOM == aStartBevelSide) { + bottomRect.x += aStartBevelOffset - startBevel; + bottomRect.width -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_BOTTOM == aEndBevelSide) { + bottomRect.width -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, bottomRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + else { // left, right + nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel); + + nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height); + if (NS_SIDE_LEFT == aStartBevelSide) { + leftRect.y += aStartBevelOffset - startBevel; + leftRect.height -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_LEFT == aEndBevelSide) { + leftRect.height -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, leftRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + + nscoord widthOffset = aBorder.width - thirdWidth; + nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height); + if (NS_SIDE_RIGHT == aStartBevelSide) { + rightRect.y += aStartBevelOffset - startBevel; + rightRect.height -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_RIGHT == aEndBevelSide) { + rightRect.height -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, rightRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + break; + } + // else fall through to solid + case NS_STYLE_BORDER_STYLE_SOLID: + DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, + aStartBevelOffset, aEndBevelSide, aEndBevelOffset); + break; + case NS_STYLE_BORDER_STYLE_BG_OUTSET: + case NS_STYLE_BORDER_STYLE_BG_INSET: + case NS_STYLE_BORDER_STYLE_OUTSET: + case NS_STYLE_BORDER_STYLE_INSET: + NS_ASSERTION(PR_FALSE, "inset, outset should have been converted to groove, ridge"); + break; + } +} + +// End table border-collapsing section + diff --git a/mozilla/layout/base/nsCSSRendering.h b/mozilla/layout/base/nsCSSRendering.h index 2e8282b0adb..360cdd17d88 100644 --- a/mozilla/layout/base/nsCSSRendering.h +++ b/mozilla/layout/base/nsCSSRendering.h @@ -180,6 +180,17 @@ public: PRIntn aSkipSides, nsRect* aGap); + // Draw a border segment in the table collapsing border model without beveling corners + static void DrawTableBorderSegment(nsIRenderingContext& aContext, + PRUint8 aBorderStyle, + nscolor aBorderColor, + const nsStyleBackground* aBGColor, + const nsRect& aBorderRect, + float aPixelsToTwips, + PRUint8 aStartBevelSide = 0, + nscoord aStartBevelOffset = 0, + PRUint8 aEndBevelSide = 0, + nscoord aEndBevelOffset = 0); /** * transform a color to a color that will show up on a printer if needed * aMapColor - color to evaluate diff --git a/mozilla/layout/base/nsLayoutAtomList.h b/mozilla/layout/base/nsLayoutAtomList.h index 8a8cfd14e5e..5cb1b488129 100644 --- a/mozilla/layout/base/nsLayoutAtomList.h +++ b/mozilla/layout/base/nsLayoutAtomList.h @@ -100,6 +100,7 @@ LAYOUT_ATOM(selectScrolledContentPseudo, ":-moz-select-scrolled-content") // Alphabetical list of frame types LAYOUT_ATOM(areaFrame, "AreaFrame") +LAYOUT_ATOM(bcTableCellFrame, "BCTableCellFrame") // table cell in border collapsing model LAYOUT_ATOM(blockFrame, "BlockFrame") LAYOUT_ATOM(boxFrame, "BoxFrame") LAYOUT_ATOM(brFrame, "BRFrame") @@ -143,7 +144,8 @@ LAYOUT_ATOM(overflowProperty, "OverflowProperty") // list of nsIFra LAYOUT_ATOM(overflowLinesProperty, "OverflowLinesProperty") // list of nsLineBox* LAYOUT_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty") // nscoord* LAYOUT_ATOM(spaceManagerProperty, "SpaceManagerProperty") // the space manager for a block -LAYOUT_ATOM(viewProperty, "ViewProperty") // nsView* +LAYOUT_ATOM(tableBCProperty, "TableBCProperty") // table border collapsing info (e.g. damage area, table border widths) +LAYOUT_ATOM(viewProperty, "ViewProperty") // Alphabetical list of event handler names LAYOUT_ATOM(onabort, "onabort") diff --git a/mozilla/layout/base/nsStyleConsts.h b/mozilla/layout/base/nsStyleConsts.h index 7390e932a0c..888a2eb9c22 100644 --- a/mozilla/layout/base/nsStyleConsts.h +++ b/mozilla/layout/base/nsStyleConsts.h @@ -249,6 +249,9 @@ #define NS_STYLE_BORDER_STYLE_HIDDEN 10 #define NS_STYLE_BORDER_STYLE_BG_INSET 11 #define NS_STYLE_BORDER_STYLE_BG_OUTSET 12 +// a bit ORed onto the style for table border collapsing indicating that the style was +// derived from a table with its rules attribute set +#define NS_STYLE_BORDER_STYLE_RULES_MASK 0x10 // See nsStyleDisplay #define NS_STYLE_CLEAR_NONE 0 diff --git a/mozilla/layout/base/public/nsHTMLReflowState.h b/mozilla/layout/base/public/nsHTMLReflowState.h index e3b27df7c2d..d6afdf798cb 100644 --- a/mozilla/layout/base/public/nsHTMLReflowState.h +++ b/mozilla/layout/base/public/nsHTMLReflowState.h @@ -270,12 +270,13 @@ struct nsHTMLReflowState { // Initialize a reflow state for a child frames reflow. Some state // is copied from the parent reflow state; the remaining state is - // computed. + // computed. nsHTMLReflowState(nsIPresContext* aPresContext, const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const nsSize& aAvailableSpace, - nsReflowReason aReason); + nsReflowReason aReason, + PRBool aInit = PR_TRUE); // Same as the previous except that the reason is taken from the // parent's reflow state. @@ -293,6 +294,13 @@ struct nsHTMLReflowState { nscoord aContainingBlockWidth, nscoord aContainingBlockHeight); + // This method initializes various data members. It is automatically + // called by the various constructors + void Init(nsIPresContext* aPresContext, + nscoord aContainingBlockWidth = -1, + nscoord aContainingBlockHeight = -1, + nsMargin* aBorder = nsnull, + nsMargin* aPadding = nsnull); /** * Get the containing block reflow state, starting from a frames * parent reflow state (the parent reflow state may or may not end @@ -352,15 +360,12 @@ struct nsHTMLReflowState { protected: - // This method initializes various data members. It is automatically - // called by the various constructors - void Init(nsIPresContext* aPresContext, - nscoord aContainingBlockWidth = -1, - nscoord aContainingBlockHeight = -1); void InitConstraints(nsIPresContext* aPresContext, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight); + nscoord aContainingBlockHeight, + nsMargin* aBorder, + nsMargin* aPadding); void CalculateHypotheticalBox(nsIPresContext* aPresContext, nsIFrame* aPlaceholderFrame, diff --git a/mozilla/layout/base/public/nsStyleConsts.h b/mozilla/layout/base/public/nsStyleConsts.h index 7390e932a0c..888a2eb9c22 100644 --- a/mozilla/layout/base/public/nsStyleConsts.h +++ b/mozilla/layout/base/public/nsStyleConsts.h @@ -249,6 +249,9 @@ #define NS_STYLE_BORDER_STYLE_HIDDEN 10 #define NS_STYLE_BORDER_STYLE_BG_INSET 11 #define NS_STYLE_BORDER_STYLE_BG_OUTSET 12 +// a bit ORed onto the style for table border collapsing indicating that the style was +// derived from a table with its rules attribute set +#define NS_STYLE_BORDER_STYLE_RULES_MASK 0x10 // See nsStyleDisplay #define NS_STYLE_CLEAR_NONE 0 diff --git a/mozilla/layout/generic/nsFrame.cpp b/mozilla/layout/generic/nsFrame.cpp index d6b48651594..ada4ba390b8 100644 --- a/mozilla/layout/generic/nsFrame.cpp +++ b/mozilla/layout/generic/nsFrame.cpp @@ -4646,6 +4646,7 @@ void DR_State::InitFrameTypeTable() AddFrameTypeInfo(nsLayoutAtoms::scrollFrame, "scroll", "scroll"); AddFrameTypeInfo(nsLayoutAtoms::tableCaptionFrame, "caption", "tableCaption"); AddFrameTypeInfo(nsLayoutAtoms::tableCellFrame, "cell", "tableCell"); + AddFrameTypeInfo(nsLayoutAtoms::bcTableCellFrame, "bcCell", "bcTableCell"); AddFrameTypeInfo(nsLayoutAtoms::tableColFrame, "col", "tableCol"); AddFrameTypeInfo(nsLayoutAtoms::tableColGroupFrame, "colG", "tableColGroup"); AddFrameTypeInfo(nsLayoutAtoms::tableFrame, "tbl", "table"); diff --git a/mozilla/layout/generic/nsHTMLParts.h b/mozilla/layout/generic/nsHTMLParts.h index 0085c46ff77..0e07379bbee 100644 --- a/mozilla/layout/generic/nsHTMLParts.h +++ b/mozilla/layout/generic/nsHTMLParts.h @@ -204,7 +204,7 @@ extern nsresult NS_NewTableColFrame(nsIPresShell* aPresShell, nsIFrame** aResult extern nsresult NS_NewTableColGroupFrame(nsIPresShell* aPresShell, nsIFrame** aResult); extern nsresult NS_NewTableRowFrame(nsIPresShell* aPresShell, nsIFrame** aResult); extern nsresult NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsIFrame** aResult); -extern nsresult NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aResult); +extern nsresult NS_NewTableCellFrame(nsIPresShell* aPresShell, PRBool aIsBorderCollapse, nsIFrame** aResult); // XXX passing aWebShell into this is wrong extern nsresult NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult, diff --git a/mozilla/layout/generic/nsHTMLReflowState.cpp b/mozilla/layout/generic/nsHTMLReflowState.cpp index 8d6855f27a5..92860c7cc46 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.cpp +++ b/mozilla/layout/generic/nsHTMLReflowState.cpp @@ -51,6 +51,9 @@ #include "nsIPref.h" #include "nsIServiceManager.h" +#define IS_TABLE_CELL(frameType)\ +((nsLayoutAtoms::tableCellFrame == frameType) || (nsLayoutAtoms::bcTableCellFrame == frameType)) + #ifdef NS_DEBUG #undef NOISY_VERTICAL_ALIGN #else @@ -140,7 +143,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const nsSize& aAvailableSpace, - nsReflowReason aReason) + nsReflowReason aReason, + PRBool aInit) : mReflowDepth(aParentReflowState.mReflowDepth + 1), mFlags(aParentReflowState.mFlags) { @@ -159,7 +163,9 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage; mPercentHeightObserver = aParentReflowState.mPercentHeightObserver; - Init(aPresContext); + if (aInit) { + Init(aPresContext); + } #ifdef IBMBIDI mRightEdge = aParentReflowState.mRightEdge; @@ -228,7 +234,9 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, void nsHTMLReflowState::Init(nsIPresContext* aPresContext, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight) + nscoord aContainingBlockHeight, + nsMargin* aBorder, + nsMargin* aPadding) { mCompactMarginWidth = 0; #ifdef DEBUG @@ -244,7 +252,7 @@ nsHTMLReflowState::Init(nsIPresContext* aPresContext, GetStyleData(frame, &mStyleText); mFrameType = DetermineFrameType(frame, mStyleDisplay); - InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight); + InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding); } const nsHTMLReflowState* @@ -261,7 +269,7 @@ nsHTMLReflowState::GetContainingBlockReflowState(const nsHTMLReflowState* aParen if (aParentRS->parentReflowState) { nsCOMPtr fType; aParentRS->parentReflowState->frame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType.get()) { + if (IS_TABLE_CELL(fType.get())) { aParentRS = aParentRS->parentReflowState; } } @@ -1575,7 +1583,9 @@ static PRBool BlinkIsAllowed(void) void nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight) + nscoord aContainingBlockHeight, + nsMargin* aBorder, + nsMargin* aPadding) { // If this is the root frame, then set the computed width and // height equal to the available space @@ -1633,7 +1643,7 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, } else { cbrs->frame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType.get()) { + if (IS_TABLE_CELL(fType.get())) { // use the cell's computed height aContainingBlockHeight = ((nsHTMLReflowState*)cbrs)->mComputedHeight; @@ -1647,10 +1657,26 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, // XXX fix to provide 0,0 for the top&bottom margins for // inline-non-replaced elements ComputeMargin(aContainingBlockWidth, cbrs); - ComputePadding(aContainingBlockWidth, cbrs); - if (!mStyleBorder->GetBorder(mComputedBorderPadding)) { - // CSS2 has no percentage borders - mComputedBorderPadding.SizeTo(0, 0, 0, 0); + if (aPadding) { // padding is an input arg + mComputedPadding.top = aPadding->top; + mComputedPadding.right = aPadding->right; + mComputedPadding.bottom = aPadding->bottom; + mComputedPadding.left = aPadding->left; + } + else { + ComputePadding(aContainingBlockWidth, cbrs); + } + if (aBorder) { // border is an input arg + mComputedBorderPadding.top = aBorder->top; + mComputedBorderPadding.right = aBorder->right; + mComputedBorderPadding.bottom = aBorder->bottom; + mComputedBorderPadding.left = aBorder->left; + } + else { + if (!mStyleBorder->GetBorder(mComputedBorderPadding)) { + // CSS2 has no percentage borders + mComputedBorderPadding.SizeTo(0, 0, 0, 0); + } } mComputedBorderPadding += mComputedPadding; diff --git a/mozilla/layout/generic/nsHTMLReflowState.h b/mozilla/layout/generic/nsHTMLReflowState.h index e3b27df7c2d..d6afdf798cb 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.h +++ b/mozilla/layout/generic/nsHTMLReflowState.h @@ -270,12 +270,13 @@ struct nsHTMLReflowState { // Initialize a reflow state for a child frames reflow. Some state // is copied from the parent reflow state; the remaining state is - // computed. + // computed. nsHTMLReflowState(nsIPresContext* aPresContext, const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const nsSize& aAvailableSpace, - nsReflowReason aReason); + nsReflowReason aReason, + PRBool aInit = PR_TRUE); // Same as the previous except that the reason is taken from the // parent's reflow state. @@ -293,6 +294,13 @@ struct nsHTMLReflowState { nscoord aContainingBlockWidth, nscoord aContainingBlockHeight); + // This method initializes various data members. It is automatically + // called by the various constructors + void Init(nsIPresContext* aPresContext, + nscoord aContainingBlockWidth = -1, + nscoord aContainingBlockHeight = -1, + nsMargin* aBorder = nsnull, + nsMargin* aPadding = nsnull); /** * Get the containing block reflow state, starting from a frames * parent reflow state (the parent reflow state may or may not end @@ -352,15 +360,12 @@ struct nsHTMLReflowState { protected: - // This method initializes various data members. It is automatically - // called by the various constructors - void Init(nsIPresContext* aPresContext, - nscoord aContainingBlockWidth = -1, - nscoord aContainingBlockHeight = -1); void InitConstraints(nsIPresContext* aPresContext, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight); + nscoord aContainingBlockHeight, + nsMargin* aBorder, + nsMargin* aPadding); void CalculateHypotheticalBox(nsIPresContext* aPresContext, nsIFrame* aPlaceholderFrame, diff --git a/mozilla/layout/html/base/src/nsFrame.cpp b/mozilla/layout/html/base/src/nsFrame.cpp index d6b48651594..ada4ba390b8 100644 --- a/mozilla/layout/html/base/src/nsFrame.cpp +++ b/mozilla/layout/html/base/src/nsFrame.cpp @@ -4646,6 +4646,7 @@ void DR_State::InitFrameTypeTable() AddFrameTypeInfo(nsLayoutAtoms::scrollFrame, "scroll", "scroll"); AddFrameTypeInfo(nsLayoutAtoms::tableCaptionFrame, "caption", "tableCaption"); AddFrameTypeInfo(nsLayoutAtoms::tableCellFrame, "cell", "tableCell"); + AddFrameTypeInfo(nsLayoutAtoms::bcTableCellFrame, "bcCell", "bcTableCell"); AddFrameTypeInfo(nsLayoutAtoms::tableColFrame, "col", "tableCol"); AddFrameTypeInfo(nsLayoutAtoms::tableColGroupFrame, "colG", "tableColGroup"); AddFrameTypeInfo(nsLayoutAtoms::tableFrame, "tbl", "table"); diff --git a/mozilla/layout/html/base/src/nsHTMLParts.h b/mozilla/layout/html/base/src/nsHTMLParts.h index 0085c46ff77..0e07379bbee 100644 --- a/mozilla/layout/html/base/src/nsHTMLParts.h +++ b/mozilla/layout/html/base/src/nsHTMLParts.h @@ -204,7 +204,7 @@ extern nsresult NS_NewTableColFrame(nsIPresShell* aPresShell, nsIFrame** aResult extern nsresult NS_NewTableColGroupFrame(nsIPresShell* aPresShell, nsIFrame** aResult); extern nsresult NS_NewTableRowFrame(nsIPresShell* aPresShell, nsIFrame** aResult); extern nsresult NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsIFrame** aResult); -extern nsresult NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aResult); +extern nsresult NS_NewTableCellFrame(nsIPresShell* aPresShell, PRBool aIsBorderCollapse, nsIFrame** aResult); // XXX passing aWebShell into this is wrong extern nsresult NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult, diff --git a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp index 8d6855f27a5..92860c7cc46 100644 --- a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp +++ b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp @@ -51,6 +51,9 @@ #include "nsIPref.h" #include "nsIServiceManager.h" +#define IS_TABLE_CELL(frameType)\ +((nsLayoutAtoms::tableCellFrame == frameType) || (nsLayoutAtoms::bcTableCellFrame == frameType)) + #ifdef NS_DEBUG #undef NOISY_VERTICAL_ALIGN #else @@ -140,7 +143,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, const nsHTMLReflowState& aParentReflowState, nsIFrame* aFrame, const nsSize& aAvailableSpace, - nsReflowReason aReason) + nsReflowReason aReason, + PRBool aInit) : mReflowDepth(aParentReflowState.mReflowDepth + 1), mFlags(aParentReflowState.mFlags) { @@ -159,7 +163,9 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage; mPercentHeightObserver = aParentReflowState.mPercentHeightObserver; - Init(aPresContext); + if (aInit) { + Init(aPresContext); + } #ifdef IBMBIDI mRightEdge = aParentReflowState.mRightEdge; @@ -228,7 +234,9 @@ nsHTMLReflowState::nsHTMLReflowState(nsIPresContext* aPresContext, void nsHTMLReflowState::Init(nsIPresContext* aPresContext, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight) + nscoord aContainingBlockHeight, + nsMargin* aBorder, + nsMargin* aPadding) { mCompactMarginWidth = 0; #ifdef DEBUG @@ -244,7 +252,7 @@ nsHTMLReflowState::Init(nsIPresContext* aPresContext, GetStyleData(frame, &mStyleText); mFrameType = DetermineFrameType(frame, mStyleDisplay); - InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight); + InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding); } const nsHTMLReflowState* @@ -261,7 +269,7 @@ nsHTMLReflowState::GetContainingBlockReflowState(const nsHTMLReflowState* aParen if (aParentRS->parentReflowState) { nsCOMPtr fType; aParentRS->parentReflowState->frame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType.get()) { + if (IS_TABLE_CELL(fType.get())) { aParentRS = aParentRS->parentReflowState; } } @@ -1575,7 +1583,9 @@ static PRBool BlinkIsAllowed(void) void nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight) + nscoord aContainingBlockHeight, + nsMargin* aBorder, + nsMargin* aPadding) { // If this is the root frame, then set the computed width and // height equal to the available space @@ -1633,7 +1643,7 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, } else { cbrs->frame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType.get()) { + if (IS_TABLE_CELL(fType.get())) { // use the cell's computed height aContainingBlockHeight = ((nsHTMLReflowState*)cbrs)->mComputedHeight; @@ -1647,10 +1657,26 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, // XXX fix to provide 0,0 for the top&bottom margins for // inline-non-replaced elements ComputeMargin(aContainingBlockWidth, cbrs); - ComputePadding(aContainingBlockWidth, cbrs); - if (!mStyleBorder->GetBorder(mComputedBorderPadding)) { - // CSS2 has no percentage borders - mComputedBorderPadding.SizeTo(0, 0, 0, 0); + if (aPadding) { // padding is an input arg + mComputedPadding.top = aPadding->top; + mComputedPadding.right = aPadding->right; + mComputedPadding.bottom = aPadding->bottom; + mComputedPadding.left = aPadding->left; + } + else { + ComputePadding(aContainingBlockWidth, cbrs); + } + if (aBorder) { // border is an input arg + mComputedBorderPadding.top = aBorder->top; + mComputedBorderPadding.right = aBorder->right; + mComputedBorderPadding.bottom = aBorder->bottom; + mComputedBorderPadding.left = aBorder->left; + } + else { + if (!mStyleBorder->GetBorder(mComputedBorderPadding)) { + // CSS2 has no percentage borders + mComputedBorderPadding.SizeTo(0, 0, 0, 0); + } } mComputedBorderPadding += mComputedPadding; diff --git a/mozilla/layout/html/document/src/html.css b/mozilla/layout/html/document/src/html.css index a501e25a540..61190f734f4 100644 --- a/mozilla/layout/html/document/src/html.css +++ b/mozilla/layout/html/document/src/html.css @@ -168,6 +168,10 @@ table[align="right"] { text-align: start; } +table[rules] { + border-collapse: collapse; +} + *|*:table-outer { display: table; margin: 0; @@ -219,6 +223,11 @@ tr { col, *|*:table-column { display: table-column; + border: inherit; + width: inherit; + height: inherit; + background: inherit; + border: inherit; } colgroup, *|*:table-column-group { diff --git a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp index e20226d01b6..113297119be 100644 --- a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -52,8 +52,11 @@ #include "nsIPresContext.h" #include "nsILinkHandler.h" #include "nsIDocument.h" +#include "nsTableFrame.h" #include "nsTableColGroupFrame.h" #include "nsTableColFrame.h" +#include "nsIDOMHTMLTableColElement.h" +#include "nsTableCellFrame.h" // to get IS_CELL_FRAME #include "nsHTMLIIDs.h" #include "nsIStyleFrameConstruction.h" #include "nsHTMLParts.h" @@ -859,6 +862,23 @@ nsFrameConstructorSaveState::~nsFrameConstructorSaveState() } } + +static +PRBool IsBorderCollapse(nsIFrame* aFrame) +{ + nsIFrame* frame = aFrame; + while (frame) { + nsCOMPtr fType; + frame->GetFrameType(getter_AddRefs(fType)); + if (nsLayoutAtoms::tableFrame == fType.get()) { + return ((nsTableFrame*)frame)->IsBorderCollapse(); + } + frame->GetParent(&frame); + } + NS_ASSERTION(PR_FALSE, "program error"); + return PR_FALSE; +} + // ----------------------------------------------------------- // Structure used when creating table frames. @@ -870,7 +890,7 @@ struct nsTableCreator { virtual nsresult CreateTableColFrame(nsIFrame** aNewFrame); virtual nsresult CreateTableColGroupFrame(nsIFrame** aNewFrame); virtual nsresult CreateTableRowFrame(nsIFrame** aNewFrame); - virtual nsresult CreateTableCellFrame(nsIFrame** aNewFrame); + virtual nsresult CreateTableCellFrame(nsIFrame* aParentFrame, nsIFrame** aNewFrame); virtual nsresult CreateTableCellInnerFrame(nsIFrame** aNewFrame); nsTableCreator(nsIPresShell* aPresShell) @@ -919,8 +939,9 @@ nsTableCreator::CreateTableRowFrame(nsIFrame** aNewFrame) { } nsresult -nsTableCreator::CreateTableCellFrame(nsIFrame** aNewFrame) { - return NS_NewTableCellFrame(mPresShell, aNewFrame); +nsTableCreator::CreateTableCellFrame(nsIFrame* aParentFrame, + nsIFrame** aNewFrame) { + return NS_NewTableCellFrame(mPresShell, IsBorderCollapse(aParentFrame), aNewFrame); } nsresult @@ -1527,7 +1548,7 @@ IsTableRelated(nsIAtom* aParentType, ((nsLayoutAtoms::tableCaptionFrame == aParentType) || (nsLayoutAtoms::tableColGroupFrame == aParentType) || (nsLayoutAtoms::tableColFrame == aParentType) || - (nsLayoutAtoms::tableCellFrame == aParentType))) { + IS_TABLE_CELL(aParentType))) { return PR_TRUE; } else return PR_FALSE; @@ -1629,7 +1650,7 @@ ProcessPseudoFrames(nsIPresContext* aPresContext, if (aPseudoFrames.mCellOuter.mFrame) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; } if (aPseudoFrames.mRow.mFrame) { rv = ProcessPseudoFrame(aPresContext, aPseudoFrames.mRow, aHighestFrame); @@ -1650,7 +1671,7 @@ ProcessPseudoFrames(nsIPresContext* aPresContext, } if (aPseudoFrames.mCellOuter.mFrame) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; } if (aPseudoFrames.mRow.mFrame) { rv = ProcessPseudoFrame(aPresContext, aPseudoFrames.mRow, aHighestFrame); @@ -1671,12 +1692,12 @@ ProcessPseudoFrames(nsIPresContext* aPresContext, } if (aPseudoFrames.mCellOuter.mFrame) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; } } - else if (nsLayoutAtoms::tableCellFrame == aPseudoFrames.mLowestType) { + else if (IS_TABLE_CELL(aPseudoFrames.mLowestType)) { rv = ProcessPseudoCellFrame(aPresContext, aPseudoFrames, aHighestFrame); - if (nsLayoutAtoms::tableCellFrame == aHighestType) return rv; + if (IS_TABLE_CELL(aHighestType)) return rv; if (aPseudoFrames.mRow.mFrame) { rv = ProcessPseudoFrame(aPresContext, aPseudoFrames.mRow, aHighestFrame); @@ -1942,6 +1963,7 @@ nsCSSFrameConstructor::CreatePseudoCellFrame(nsIPresShell* aPresShell // set pseudo data for the newly created frames pseudoOuter.mChildList.AddChild(pseudoInner.mFrame); + // give it nsLayoutAtoms::tableCellFrame, if it is really nsLayoutAtoms::bcTableCellFrame, it will match later aState.mPseudoFrames.mLowestType = nsLayoutAtoms::tableCellFrame; // set pseudo data for the parent @@ -2021,7 +2043,7 @@ nsCSSFrameConstructor::GetPseudoColGroupFrame(nsIPresShell* aPresShel rv = CreatePseudoCellFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); created = PR_TRUE; } - if (created || (nsLayoutAtoms::tableCellFrame == parentFrameType.get()) || // cell parent + if (created || IS_TABLE_CELL(parentFrameType.get()) || // cell parent !IsTableRelated(parentFrameType.get(), PR_TRUE)) { // block parent rv = CreatePseudoTableFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); } @@ -2065,7 +2087,7 @@ nsCSSFrameConstructor::GetPseudoRowGroupFrame(nsIPresShell* aPresShel rv = CreatePseudoCellFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); created = PR_TRUE; } - if (created || (nsLayoutAtoms::tableCellFrame == parentFrameType.get()) || // cell parent + if (created || IS_TABLE_CELL(parentFrameType.get()) || // cell parent !IsTableRelated(parentFrameType.get(), PR_TRUE)) { // block parent rv = CreatePseudoTableFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); } @@ -2102,7 +2124,7 @@ nsCSSFrameConstructor::GetPseudoRowFrame(nsIPresShell* aPresShell, if (pseudoFrames.IsEmpty()) { PRBool created = PR_FALSE; - if ((nsLayoutAtoms::tableCellFrame == parentFrameType.get()) || // cell parent + if (IS_TABLE_CELL(parentFrameType.get()) || // cell parent !IsTableRelated(parentFrameType.get(), PR_TRUE)) { // block parent rv = CreatePseudoTableFrame(aPresShell, aPresContext, aTableCreator, aState, &aParentFrameIn); created = PR_TRUE; @@ -2230,7 +2252,7 @@ nsCSSFrameConstructor::GetParentFrame(nsIPresShell* aPresShell, pseudoParentFrame = pseudoFrames.mRowGroup.mFrame; } } - else if (nsLayoutAtoms::tableCellFrame == aChildFrameType) { // cell child + else if (IS_TABLE_CELL(aChildFrameType)) { // cell child if (nsLayoutAtoms::tableRowFrame != parentFrameType.get()) { // need pseudo row parent rv = GetPseudoRowFrame(aPresShell, aPresContext, aTableCreator, aState, aParentFrameIn); if (NS_FAILED(rv)) return rv; @@ -2572,7 +2594,7 @@ nsCSSFrameConstructor::ConstructTableColFrame(nsIPresShell* aPresShel PRBool& aIsPseudoParent) { nsresult rv = NS_OK; - if (!aPresShell || !aPresContext || !aParentFrameIn) return rv; + if (!aPresShell || !aPresContext || !aParentFrameIn || !aStyleContext) return rv; nsIFrame* parentFrame = aParentFrameIn; aIsPseudoParent = PR_FALSE; @@ -2585,16 +2607,42 @@ nsCSSFrameConstructor::ConstructTableColFrame(nsIPresShell* aPresShel } } - rv = aTableCreator.CreateTableColFrame(&aNewFrame); - if (NS_FAILED(rv)) return rv; - InitAndRestoreFrame(aPresContext, aState, aContent, - parentFrame, aStyleContext, nsnull, aNewFrame); + rv = aTableCreator.CreateTableColFrame(&aNewFrame); if (NS_FAILED(rv)) return rv; + InitAndRestoreFrame(aPresContext, aState, aContent, parentFrame, aStyleContext, nsnull, aNewFrame); + // if the parent frame was anonymous then reparent the style context + nsCOMPtr parentStyleContext; + parentFrame->GetStyleContext(getter_AddRefs(parentStyleContext)); + if (aIsPseudoParent) { + aPresContext->ReParentStyleContext(aNewFrame, parentStyleContext); + } + + // construct additional col frames if the col frame has a span > 1 + PRInt32 span = 1; + nsCOMPtr cgContent(do_QueryInterface(aContent)); + if (cgContent) { + cgContent->GetSpan(&span); + nsIFrame* lastCol = aNewFrame; + for (PRInt32 spanX = 1; spanX < span; spanX++) { + nsCOMPtr styleContext; + aPresContext->ResolvePseudoStyleContextFor(aContent, nsHTMLAtoms::tableColPseudo, aStyleContext, + PR_FALSE, getter_AddRefs(styleContext)); + nsIFrame* newCol; + rv = aTableCreator.CreateTableColFrame(&newCol); if (NS_FAILED(rv)) return rv; + InitAndRestoreFrame(aPresContext, aState, aContent, parentFrame, styleContext, nsnull, newCol); + if (aIsPseudoParent) { + aPresContext->ReParentStyleContext(newCol, aStyleContext); + } + ((nsTableColFrame*)newCol)->SetType(eColAnonymousCol); + lastCol->SetNextSibling(newCol); + lastCol = newCol; + } + } + if (!aIsPseudo) { nsFrameItems childItems; nsIFrame* captionFrame; rv = TableProcessChildren(aPresShell, aPresContext, aState, aContent, aNewFrame, - aTableCreator, childItems, captionFrame); - if (NS_FAILED(rv)) return rv; + aTableCreator, childItems, captionFrame); if (NS_FAILED(rv)) return rv; aNewFrame->SetInitialChildList(aPresContext, nsnull, childItems.childList); if (aIsPseudoParent) { aState.mPseudoFrames.mColGroup.mChildList.AddChild(aNewFrame); @@ -2625,6 +2673,7 @@ nsCSSFrameConstructor::ConstructTableCellFrame(nsIPresShell* aPresShe aIsPseudoParent = PR_FALSE; if (!aIsPseudo) { // this frame may have a pseudo parent + // use nsLayoutAtoms::tableCellFrame which will match if it is really nsLayoutAtoms::bcTableCellFrame GetParentFrame(aPresShell, aPresContext, aTableCreator, *aParentFrameIn, nsLayoutAtoms::tableCellFrame, aState, parentFrame, aIsPseudoParent); if (!aIsPseudoParent && !aState.mPseudoFrames.IsEmpty()) { @@ -2635,7 +2684,7 @@ nsCSSFrameConstructor::ConstructTableCellFrame(nsIPresShell* aPresShe } } - rv = aTableCreator.CreateTableCellFrame(&aNewCellOuterFrame); + rv = aTableCreator.CreateTableCellFrame(parentFrame, &aNewCellOuterFrame); if (NS_FAILED(rv)) return rv; // Initialize the table cell frame @@ -11328,7 +11377,7 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsIPresShell* aPresShell, // See if it's a table cell frame cellFrame->GetFrameType(&tableType); - if (nsLayoutAtoms::tableCellFrame == tableType) { + if (IS_TABLE_CELL(tableType)) { nsIFrame* continuingCellFrame; CreateContinuingFrame(aPresShell, aPresContext, cellFrame, newFrame, &continuingCellFrame); @@ -11343,8 +11392,8 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsIPresShell* aPresShell, newFrame->SetInitialChildList(aPresContext, nsnull, newChildList.childList); } - } else if (nsLayoutAtoms::tableCellFrame == frameType) { - rv = NS_NewTableCellFrame(aPresShell, &newFrame); + } else if (IS_TABLE_CELL(frameType)) { + rv = NS_NewTableCellFrame(aPresShell, IsBorderCollapse(aParentFrame), &newFrame); if (NS_SUCCEEDED(rv)) { newFrame->Init(aPresContext, content, aParentFrame, styleContext, aFrame); nsHTMLContainerFrame::CreateViewForFrame(aPresContext, newFrame, diff --git a/mozilla/layout/html/style/src/nsCSSRendering.cpp b/mozilla/layout/html/style/src/nsCSSRendering.cpp index 543a729ae1f..ea8f7cdea80 100644 --- a/mozilla/layout/html/style/src/nsCSSRendering.cpp +++ b/mozilla/layout/html/style/src/nsCSSRendering.cpp @@ -514,6 +514,7 @@ void nsCSSRendering::DrawSide(nsIRenderingContext& aContext, } } + /** * Draw a dotted/dashed sides of a box */ @@ -3821,3 +3822,356 @@ void FillOrInvertRect(nsIRenderingContext& aRC, const nsRect& aRect, PRBool aInv } } +// Begin table border-collapsing section +// These functions were written to not disrupt the normal ones and yet satisfy some additional requirements +// At some point, all functions should be unified to include the additional functionality that these provide + +static nscoord +RoundIntToPixel(nscoord aValue, + nscoord aTwipsPerPixel, + PRBool aRoundDown = PR_FALSE) +{ + nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f); + nscoord extra = aValue % aTwipsPerPixel; + nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra; + + return finalValue; +} + +static nscoord +RoundFloatToPixel(float aValue, + nscoord aTwipsPerPixel, + PRBool aRoundDown = PR_FALSE) +{ + return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown); +} + +static void +SetPoly(const nsRect& aRect, + nsPoint* poly) +{ + poly[0].x = aRect.x; + poly[0].y = aRect.y; + poly[1].x = aRect.x + aRect.width; + poly[1].y = aRect.y; + poly[2].x = aRect.x + aRect.width; + poly[2].y = aRect.y + aRect.height; + poly[3].x = aRect.x; + poly[3].y = aRect.y + aRect.height; + poly[4].x = aRect.x; + poly[4].y = aRect.y; +} + +static void +DrawSolidBorderSegment(nsIRenderingContext& aContext, + nsRect aRect, + nscoord aTwipsPerPixel, + PRUint8 aStartBevelSide = 0, + nscoord aStartBevelOffset = 0, + PRUint8 aEndBevelSide = 0, + nscoord aEndBevelOffset = 0) +{ + + if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) || + ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) { + // simple line or rectangle + if ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)) { + if (1 == aRect.height) + aContext.DrawLine(aRect.x, aRect.y, aRect.x, aRect.y + aRect.height); + else + aContext.FillRect(aRect); + } + else { + if (1 == aRect.width) + aContext.DrawLine(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y); + else + aContext.FillRect(aRect); + } + } + else { + // polygon with beveling + nsPoint poly[5]; + SetPoly(aRect, poly); + switch(aStartBevelSide) { + case NS_SIDE_TOP: + poly[0].x += aStartBevelOffset; + poly[4].x = poly[0].x; + break; + case NS_SIDE_BOTTOM: + poly[3].x += aStartBevelOffset; + break; + case NS_SIDE_RIGHT: + poly[1].y += aStartBevelOffset; + break; + case NS_SIDE_LEFT: + poly[0].y += aStartBevelOffset; + poly[4].y = poly[0].y; + } + + switch(aEndBevelSide) { + case NS_SIDE_TOP: + poly[1].x -= aEndBevelOffset; + break; + case NS_SIDE_BOTTOM: + poly[2].x -= aEndBevelOffset; + break; + case NS_SIDE_RIGHT: + poly[2].y -= aEndBevelOffset; + break; + case NS_SIDE_LEFT: + poly[3].y -= aEndBevelOffset; + } + + aContext.FillPolygon(poly, 5); + } + + +} + +static void +GetDashInfo(nscoord aBorderLength, + nscoord aDashLength, + nscoord aTwipsPerPixel, + PRInt32& aNumDashSpaces, + nscoord& aStartDashLength, + nscoord& aEndDashLength) +{ + aNumDashSpaces = 0; + if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) { + aStartDashLength = aBorderLength; + aEndDashLength = 0; + } + else { + aNumDashSpaces = aBorderLength / (2 * aDashLength); // round down + nscoord foo = ((2 * aNumDashSpaces) - 1) * aDashLength; + nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength); + if (extra > 0) { + nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel); + aStartDashLength += half; + aEndDashLength += (extra - half); + } + } +} + +void +nsCSSRendering::DrawTableBorderSegment(nsIRenderingContext& aContext, + PRUint8 aBorderStyle, + nscolor aBorderColor, + const nsStyleBackground* aBGColor, + const nsRect& aBorder, + float aPixelsToTwips, + PRUint8 aStartBevelSide, + nscoord aStartBevelOffset, + PRUint8 aEndBevelSide, + nscoord aEndBevelOffset) +{ + aContext.SetColor (aBorderColor); + + PRBool horizontal = ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)); + nscoord twipsPerPixel = NSIntPixelsToTwips(1, aPixelsToTwips); + PRBool ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE; + + if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) || + (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) { + // no beveling for 1 pixel border, dash or dot + aStartBevelOffset = 0; + aEndBevelOffset = 0; + } + + switch (aBorderStyle) { + case NS_STYLE_BORDER_STYLE_NONE: + case NS_STYLE_BORDER_STYLE_HIDDEN: + case NS_STYLE_BORDER_STYLE_BLANK: + //NS_ASSERTION(PR_FALSE, "style of none, hidden, or blank"); + break; + case NS_STYLE_BORDER_STYLE_DOTTED: + case NS_STYLE_BORDER_STYLE_DASHED: + { + nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH; + // make the dash length proportional to the border thickness + dashLength *= (horizontal) ? aBorder.height : aBorder.width; + // make the min dash length for the ends 1/2 the dash length + nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) + ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength; + minDashLength = PR_MAX(minDashLength, twipsPerPixel); + nscoord numDashSpaces = 0; + nscoord startDashLength = minDashLength; + nscoord endDashLength = minDashLength; + if (horizontal) { + GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength); + nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height); + DrawSolidBorderSegment(aContext, rect, PR_TRUE); + for (PRInt32 spaceX = 0; spaceX < numDashSpaces; spaceX++) { + rect.x += rect.width + dashLength; + rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength; + DrawSolidBorderSegment(aContext, rect, PR_TRUE); + } + } + else { + GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength); + nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength); + DrawSolidBorderSegment(aContext, rect, PR_FALSE); + for (PRInt32 spaceY = 0; spaceY < numDashSpaces; spaceY++) { + rect.y += rect.height + dashLength; + rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength; + DrawSolidBorderSegment(aContext, rect, PR_FALSE); + } + } + } + break; + case NS_STYLE_BORDER_STYLE_GROOVE: + ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge + case NS_STYLE_BORDER_STYLE_RIDGE: + if ((horizontal && (twipsPerPixel >= aBorder.height)) || + (!horizontal && (twipsPerPixel >= aBorder.width))) { + // a one pixel border + DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, aStartBevelOffset, + aEndBevelSide, aEndBevelOffset); + } + else { + nscoord startBevel = (aStartBevelOffset > 0) + ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, PR_TRUE) : 0; + nscoord endBevel = (aEndBevelOffset > 0) + ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, PR_TRUE) : 0; + PRUint8 ridgeGrooveSide = (horizontal) ? NS_SIDE_TOP : NS_SIDE_LEFT; + aContext.SetColor ( + MakeBevelColor (ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor, PR_TRUE)); + nsRect rect(aBorder); + nscoord half; + if (horizontal) { // top, bottom + half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel); + rect.height = half; + if (NS_SIDE_TOP == aStartBevelSide) { + rect.x += startBevel; + rect.width -= startBevel; + } + if (NS_SIDE_TOP == aEndBevelSide) { + rect.width -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + else { // left, right + half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel); + rect.width = half; + if (NS_SIDE_LEFT == aStartBevelSide) { + rect.y += startBevel; + rect.height -= startBevel; + } + if (NS_SIDE_LEFT == aEndBevelSide) { + rect.height -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + + rect = aBorder; + ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT; + aContext.SetColor ( + MakeBevelColor (ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor, PR_TRUE)); + if (horizontal) { + rect.y = rect.y + half; + rect.height = aBorder.height - half; + if (NS_SIDE_BOTTOM == aStartBevelSide) { + rect.x += startBevel; + rect.width -= startBevel; + } + if (NS_SIDE_BOTTOM == aEndBevelSide) { + rect.width -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + else { + rect.x = rect.x + half; + rect.width = aBorder.width - half; + if (NS_SIDE_RIGHT == aStartBevelSide) { + rect.y += aStartBevelOffset - startBevel; + rect.height -= startBevel; + } + if (NS_SIDE_RIGHT == aEndBevelSide) { + rect.height -= endBevel; + } + DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + } + break; + case NS_STYLE_BORDER_STYLE_DOUBLE: + if ((aBorder.width > 2) && (aBorder.height > 2)) { + nscoord startBevel = (aStartBevelOffset > 0) + ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0; + nscoord endBevel = (aEndBevelOffset > 0) + ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0; + if (horizontal) { // top, bottom + nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel); + + // draw the top line or rect + nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight); + if (NS_SIDE_TOP == aStartBevelSide) { + topRect.x += aStartBevelOffset - startBevel; + topRect.width -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_TOP == aEndBevelSide) { + topRect.width -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, topRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + + // draw the botom line or rect + nscoord heightOffset = aBorder.height - thirdHeight; + nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset); + if (NS_SIDE_BOTTOM == aStartBevelSide) { + bottomRect.x += aStartBevelOffset - startBevel; + bottomRect.width -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_BOTTOM == aEndBevelSide) { + bottomRect.width -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, bottomRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + else { // left, right + nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel); + + nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height); + if (NS_SIDE_LEFT == aStartBevelSide) { + leftRect.y += aStartBevelOffset - startBevel; + leftRect.height -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_LEFT == aEndBevelSide) { + leftRect.height -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, leftRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + + nscoord widthOffset = aBorder.width - thirdWidth; + nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height); + if (NS_SIDE_RIGHT == aStartBevelSide) { + rightRect.y += aStartBevelOffset - startBevel; + rightRect.height -= aStartBevelOffset - startBevel; + } + if (NS_SIDE_RIGHT == aEndBevelSide) { + rightRect.height -= aEndBevelOffset - endBevel; + } + DrawSolidBorderSegment(aContext, rightRect, twipsPerPixel, aStartBevelSide, + startBevel, aEndBevelSide, endBevel); + } + break; + } + // else fall through to solid + case NS_STYLE_BORDER_STYLE_SOLID: + DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, + aStartBevelOffset, aEndBevelSide, aEndBevelOffset); + break; + case NS_STYLE_BORDER_STYLE_BG_OUTSET: + case NS_STYLE_BORDER_STYLE_BG_INSET: + case NS_STYLE_BORDER_STYLE_OUTSET: + case NS_STYLE_BORDER_STYLE_INSET: + NS_ASSERTION(PR_FALSE, "inset, outset should have been converted to groove, ridge"); + break; + } +} + +// End table border-collapsing section + diff --git a/mozilla/layout/html/style/src/nsCSSRendering.h b/mozilla/layout/html/style/src/nsCSSRendering.h index 2e8282b0adb..360cdd17d88 100644 --- a/mozilla/layout/html/style/src/nsCSSRendering.h +++ b/mozilla/layout/html/style/src/nsCSSRendering.h @@ -180,6 +180,17 @@ public: PRIntn aSkipSides, nsRect* aGap); + // Draw a border segment in the table collapsing border model without beveling corners + static void DrawTableBorderSegment(nsIRenderingContext& aContext, + PRUint8 aBorderStyle, + nscolor aBorderColor, + const nsStyleBackground* aBGColor, + const nsRect& aBorderRect, + float aPixelsToTwips, + PRUint8 aStartBevelSide = 0, + nscoord aStartBevelOffset = 0, + PRUint8 aEndBevelSide = 0, + nscoord aEndBevelOffset = 0); /** * transform a color to a color that will show up on a printer if needed * aMapColor - color to evaluate diff --git a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp index 479cc30158f..b79d4379a67 100644 --- a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp +++ b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp @@ -139,7 +139,7 @@ PRBool BasicTableLayoutStrategy::Initialize(nsIPresContext* aPresContex nscoord minWidth, prefWidth; mTableFrame->CalcMinAndPreferredWidths(aPresContext, aReflowState, PR_FALSE, minWidth, prefWidth); if (hasPctCol && mTableFrame->IsAutoWidth()) { - prefWidth = CalcPctAdjTableWidth(aReflowState, boxWidth, p2t); + prefWidth = CalcPctAdjTableWidth(*aPresContext, aReflowState, boxWidth, p2t); } // calc the desired width, considering if there is a specified width. // don't use nsTableFrame::CalcDesiredWidth because it is based on table column widths. @@ -195,7 +195,7 @@ ResetPctValues(nsTableFrame* aTableFrame, // initialize the col percent and cell percent values to 0. PRInt32 colX; for (colX = 0; colX < aNumCols; colX++) { - nsTableColFrame* colFrame = aTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = aTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (colFrame) { colFrame->SetWidth(PCT, WIDTH_NOT_SET); colFrame->SetWidth(PCT_ADJ, WIDTH_NOT_SET); @@ -207,6 +207,7 @@ PRBool BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState) { + if (!aPresContext) ABORT1(PR_FALSE); #ifdef DEBUG_TABLE_STRATEGY printf("BalanceColumnWidths en count=%d \n", gsDebugCount++); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE); #endif @@ -220,8 +221,16 @@ BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresCont PRInt32 numCols = mTableFrame->GetColCount(); PRBool tableIsAutoWidth = mTableFrame->IsAutoWidth(); - nscoord horBorderPadding = aReflowState.mComputedBorderPadding.left + - aReflowState.mComputedBorderPadding.right; + + nscoord horOffset; + // get the reduction in available horizontal space due to borders and padding + if (mTableFrame->IsBorderCollapse()) { + nsMargin offset = mTableFrame->GetChildAreaOffset(*aPresContext, &aReflowState); + horOffset = offset.left + offset.right; + } + else { + horOffset = aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; + } // determine if the table is auto/fixed and get the fixed width if available nscoord maxWidth = mTableFrame->CalcBorderBoxWidth(aPresContext, aReflowState); @@ -238,19 +247,19 @@ BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresCont // An auto table returns a new table width based on percent cells/cols if they exist nscoord perAdjTableWidth = 0; if (mTableFrame->HasPctCol()) { - perAdjTableWidth = AssignPctColumnWidths(aReflowState, maxWidth, tableIsAutoWidth, p2t); + perAdjTableWidth = AssignPctColumnWidths(*aPresContext, aReflowState, maxWidth, tableIsAutoWidth, p2t); if (perAdjTableWidth > 0) { // if an auto table has a pct col or cell, set the preferred table width // here so that CalcPctAdjTableWidth wont't need to be called by the table mTableFrame->SetPreferredWidth(perAdjTableWidth); } perAdjTableWidth = PR_MIN(perAdjTableWidth, maxWidth); - perAdjTableWidth -= horBorderPadding; + perAdjTableWidth -= horOffset; perAdjTableWidth = PR_MAX(perAdjTableWidth, 0); } // reduce the maxWidth by border and padding, since we will be dealing with content width - maxWidth -= horBorderPadding; + maxWidth -= horOffset; maxWidth = PR_MAX(0, maxWidth); PRInt32 numNonZeroWidthCols = 0; @@ -258,7 +267,7 @@ BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresCont nscoord minTableWidth = 0; PRInt32 colX; for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord colMinWidth = colFrame->GetMinWidth(); mTableFrame->SetColumnWidth(colX, colMinWidth); @@ -400,7 +409,7 @@ void BasicTableLayoutStrategy::AllocateFully(nscoord& aTotalAllocated, { PRInt32 numCols = mTableFrame->GetColCount(); for (PRInt32 colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; @@ -454,7 +463,7 @@ void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, } else { if (aExclude0Pro) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (colFrame->GetConstraint() == e0ProportionConstraint) { aAllocTypes[colX] = FINISHED; } @@ -467,7 +476,7 @@ void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, PRInt32 numColsAllocated = 0; PRInt32 totalAllocated = 0; for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); PRBool skipColumn = aExclude0Pro && (e0ProportionConstraint == colFrame->GetConstraint()); if (FINISHED != aAllocTypes[colX] && !skipColumn ) { divisor += mTableFrame->GetColumnWidth(colX); @@ -477,7 +486,7 @@ void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, for (colX = 0; colX < numCols; colX++) { if (FINISHED != aAllocTypes[colX]) { if (aExclude0Pro) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (colFrame && (e0ProportionConstraint == colFrame->GetConstraint())) { continue; } @@ -533,7 +542,7 @@ BasicTableLayoutStrategy::ComputeNonPctColspanWidths(const nsHTMLReflowState& aR PRInt32 colX; for (colX = numCols - 1; colX >= 0; colX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; colFrame->SetWidth(MIN_ADJ, WIDTH_NOT_SET); colFrame->SetWidth(FIX_ADJ, WIDTH_NOT_SET); @@ -698,7 +707,7 @@ BasicTableLayoutStrategy::ComputeNonPctColspanWidths(PRInt32 aWidthInd PRInt32 spanX; // accumulate the various divisors to be used later for (spanX = 0; spanX < aColSpan; spanX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord minWidth = PR_MAX(colFrame->GetMinWidth(), 0); nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0); @@ -802,7 +811,7 @@ BasicTableLayoutStrategy::ComputeNonPctColspanWidths(PRInt32 aWidthInd // get the correct numerator in a similar fashion to getting the divisor for (spanX = 0; spanX < aColSpan; spanX++) { if (usedWidth >= availWidth) break; - nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord minWidth = colFrame->GetMinWidth(); nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0); @@ -974,7 +983,7 @@ BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPre nscoord fixWidth = WIDTH_NOT_SET; // Get column frame and reset it - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; NS_ASSERTION(nsnull != colFrame, "bad col frame"); colFrame->ResetSizingInfo(); @@ -1079,7 +1088,7 @@ BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPre } //set the table col fixed width if present for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord fixColWidth = colFrame->GetWidth(FIX); // use the style width of a col only if the col hasn't gotten a fixed width from any cell @@ -1140,7 +1149,7 @@ BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPre // Set the table col width for each col to the content min. for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); nscoord minWidth = colFrame->GetMinWidth(); mTableFrame->SetColumnWidth(colX, minWidth); } @@ -1159,7 +1168,7 @@ BasicTableLayoutStrategy::ReduceOverSpecifiedPctCols(nscoord aExcess) { nscoord numCols = mTableFrame->GetColCount(); for (PRInt32 colX = numCols - 1; (colX >= 0) && (aExcess > 0); colX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; nscoord pctWidth = colFrame->GetWidth(PCT); nscoord reduction = 0; @@ -1198,7 +1207,8 @@ inline nscoord WrapupAssignPctColumnWidths(nsTableFrame* aTableFrame, #endif nscoord -BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState, +BasicTableLayoutStrategy::CalcPctAdjTableWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidthIn, float aPixelToTwips) { @@ -1219,7 +1229,7 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS rawPctValues[colX] = 0.0f; } - nsMargin borderPadding = mTableFrame->GetBorderPadding(aReflowState); + nsMargin borderPadding = mTableFrame->GetContentAreaOffset(aPresContext, &aReflowState); nscoord availWidth = aAvailWidthIn; if (NS_UNCONSTRAINEDSIZE != availWidth) { // adjust the avail width to exclude table border, padding and cell spacing @@ -1227,7 +1237,7 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS } for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(0); if (!colFrame) continue; nscoord maxColBasis = -1; // Scan the cells in the col @@ -1282,7 +1292,7 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS nscoord fixDesTotalNoPct = 0; // total of fix or des widths of cols without pct for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(0); nscoord fixWidth = colFrame->GetFixWidth(); nscoord fixDesWidth = (fixWidth > 0) ? fixWidth : colFrame->GetDesWidth(); fixDesTotal += fixDesWidth; @@ -1329,7 +1339,8 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS // Determine percentage col widths for each col frame nscoord -BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflowState, +BasicTableLayoutStrategy::AssignPctColumnWidths(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidth, PRBool aTableIsAutoWidth, float aPixelToTwips) @@ -1349,11 +1360,11 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // on percent cells/cols. This probably should only be a NavQuirks thing, since // a percentage based cell or column on an auto table should force the column to auto nscoord basis = (aTableIsAutoWidth) - ? CalcPctAdjTableWidth(aReflowState, aAvailWidth, aPixelToTwips) + ? CalcPctAdjTableWidth(aPresContext, aReflowState, aAvailWidth, aPixelToTwips) : aAvailWidth; // adjust the basis to exclude table border, padding and cell spacing - nsMargin borderPadding = mTableFrame->GetBorderPadding(aReflowState); + nsMargin borderPadding = mTableFrame->GetContentAreaOffset(aPresContext, &aReflowState); basis -= borderPadding.left + borderPadding.right + mCellSpacingTotal; nscoord colPctTotal = 0; @@ -1361,7 +1372,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // Determine the percentage contribution for cols and for cells with colspan = 1 // Iterate backwards, similarly to the reasoning in AssignNonPctColumnWidths for (colX = numCols - 1; colX >= 0; colX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(0); if (!colFrame) continue; nscoord maxColPctWidth = WIDTH_NOT_SET; float maxColPct = 0.0f; @@ -1431,7 +1442,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow } // determine if the cell spans cols which have a pct value for (PRInt32 spanX = 0; (spanX < colSpan) && !done; spanX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) ABORT1(0); if (!colFrame) continue; if (colFrame->GetWidth(PCT) > 0) { mTableFrame->SetHasCellSpanningPctCol(PR_TRUE); @@ -1511,7 +1522,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // accumulate the spanTotal as the max of MIN, DES, FIX, PCT PRInt32 spanX; for (spanX = 0; spanX < colSpan; spanX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) ABORT1(0); if (!colFrame) continue; nscoord colPctWidth = colFrame->GetWidth(PCT); if (colPctWidth > 0) { // skip pct cols @@ -1546,7 +1557,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // record the percent contributions for the spanned cols PRInt32 usedColumns = colSpan; for (spanX = colSpan-1; spanX >= 0; spanX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) ABORT1(0); if (!colFrame) continue; if ((colFrame->GetWidth(PCT) > 0) || (canSkipPctAdj && (colFrame->GetWidth(PCT_ADJ) > 0))) { // dont use pct cols or if we can skip the pct adj event do not take the PCT_ADJ cols @@ -1621,7 +1632,7 @@ void BasicTableLayoutStrategy::CalculateTotals(PRInt32* aTotalCounts, PRInt32 colX; for (colX = 0; colX < numEffCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; nscoord minCol = colFrame->GetMinWidth(); aTotalCounts[MIN_CON]++; @@ -1820,7 +1831,7 @@ void BasicTableLayoutStrategy::AllocateConstrained(PRInt32 aAvailWidth, PRInt32 colX; // find out how many constrained cols there are for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; @@ -1837,7 +1848,7 @@ void BasicTableLayoutStrategy::AllocateConstrained(PRInt32 aAvailWidth, PRInt32 constrColX = 0; // set the col info entries for each constrained col for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; diff --git a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h index 793b8b79d40..69883aabd2c 100644 --- a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h +++ b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h @@ -93,7 +93,8 @@ public: * @param aPixelToTwips - the number of twips in a pixel. * @return - the basis for percent calculations */ - virtual nscoord CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState, + virtual nscoord CalcPctAdjTableWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidth, float aPixelToTwips); void Dump(PRInt32 aIndent); @@ -159,7 +160,8 @@ protected: * @param aPixelToTwips - the number of twips in a pixel. * @return - the adjusted basis including table border, padding and cell spacing */ - nscoord AssignPctColumnWidths(const nsHTMLReflowState& aReflowState, + nscoord AssignPctColumnWidths(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aBasis, PRBool aTableIsAutoWidth, float aPixelToTwips); diff --git a/mozilla/layout/html/table/src/celldata.h b/mozilla/layout/html/table/src/celldata.h index cf977be70d6..b281df0e2e2 100644 --- a/mozilla/layout/html/table/src/celldata.h +++ b/mozilla/layout/html/table/src/celldata.h @@ -38,6 +38,7 @@ #define CellData_h__ #include "nsISupports.h" +#include "nsCoord.h" class nsTableCellFrame; @@ -51,8 +52,9 @@ public: ~CellData(); + void Init(nsTableCellFrame* aCellFrame); PRBool IsOrig() const; - + PRBool IsDead() const; PRBool IsSpan() const; PRBool IsRowSpan() const; @@ -84,6 +86,93 @@ protected: }; }; +// Border Collapsing Cell Data +enum BCBorderOwner +{ + eTableOwner = 0, + eColGroupOwner = 1, + eAjaColGroupOwner = 2, // col group to the left + eColOwner = 3, + eAjaColOwner = 4, // col to the left + eRowGroupOwner = 5, + eAjaRowGroupOwner = 6, // row group above + eRowOwner = 7, + eAjaRowOwner = 8, // row above + eCellOwner = 9, + eAjaCellOwner = 10, // cell to the top or to the left +}; + +// These are the max sizes that are stored. If they are exceeded, then the max is stored and +// the actual value is computed when needed. +#define MAX_BORDER_WIDTH 64 +#define MAX_CORNER_SUB_WIDTH 128 + +// BCData stores the top and left border info and the corner connecting the two. +class BCData +{ +public: + BCData(); + + ~BCData(); + + nscoord GetLeftEdge(BCBorderOwner& aOwner, + PRBool& aStart) const; + + void SetLeftEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart); + + nscoord GetTopEdge(BCBorderOwner& aOwner, + PRBool& aStart) const; + + void SetTopEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart); + + PRUint8 GetCorner(PRUint8& aCornerOwner, + PRPackedBool& aBevel) const; + + void SetCorner(PRUint8 aOwner, + PRUint8 aSubSize, + PRBool aBevel); + + PRBool IsLeftStart() const; + + void SetLeftStart(PRBool aValue); + + PRBool IsTopStart() const; + + void SetTopStart(PRBool aValue); + + +protected: + unsigned mLeftOwner: 4; // owner of left border + unsigned mLeftSize: 6; // size in pixels of left border + unsigned mLeftStart: 1; // set if this is the start of a vertical border segment + unsigned mCornerSide: 2; // side of the owner of the upper left corner relative to the corner + unsigned mCornerSubSize: 7; // size of the largest border not in the dominate plane (for example, if + // corner is owned by the segment to its top or bottom, then the size is the + // max of the border sizes of the segments to its left or right. + unsigned mCornerBevel: 1; // is the corner beveled (only two segments, perpendicular, not dashed or dotted). + unsigned mTopOwner: 4; // owner of top border + unsigned mTopSize: 6; // size in pixels of top border + unsigned mTopStart: 1; // set if this is the start of a horizontal border segment +}; + +// BCCellData entries replace CellData entries in the cell map if the border collapsing model is in +// effect. BCData for a row and col entry contains the left and top borders of cell at that row and +// col and the corner connecting the two. The right borders of the cells in the last col and the bottom +// borders of the last row are stored in separate BCData entries in the cell map. +class BCCellData : public CellData +{ +public: + BCCellData(nsTableCellFrame* aOrigCell); + ~BCCellData(); + + BCData mData; +}; + + #define SPAN 0x00000001 // there a row or col span #define ROW_SPAN 0x00000002 // there is a row span #define ROW_SPAN_0 0x00000004 // the row span is 0 @@ -103,9 +192,19 @@ inline nsTableCellFrame* CellData::GetCellFrame() const return nsnull; } +inline void CellData::Init(nsTableCellFrame* aCellFrame) +{ + mOrigCell = aCellFrame; +} + inline PRBool CellData::IsOrig() const { - return (SPAN != (SPAN & mBits)); + return ((nsnull != mOrigCell) && (SPAN != (SPAN & mBits))); +} + +inline PRBool CellData::IsDead() const +{ + return (0 == mBits); } inline PRBool CellData::IsSpan() const @@ -213,4 +312,88 @@ inline void CellData::SetOverlap(PRBool aOverlap) } } +inline BCData::BCData() +{ + mLeftOwner = mTopOwner = eCellOwner; + mLeftStart = mTopStart = 1; + mLeftSize = mCornerSide = mCornerSubSize = mTopSize = 0; +} + +inline BCData::~BCData() +{ +} + +inline nscoord BCData::GetLeftEdge(BCBorderOwner& aOwner, + PRBool& aStart) const +{ + aOwner = (BCBorderOwner)mLeftOwner; + aStart = (PRBool)mLeftStart; + + return (nscoord)mLeftSize; +} + +inline void BCData::SetLeftEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart) +{ + mLeftOwner = aOwner; + mLeftSize = (aSize > MAX_BORDER_WIDTH) ? MAX_BORDER_WIDTH : aSize; + mLeftStart = aStart; +} + +inline nscoord BCData::GetTopEdge(BCBorderOwner& aOwner, + PRBool& aStart) const +{ + aOwner = (BCBorderOwner)mTopOwner; + aStart = (PRBool)mTopStart; + + return (nscoord)mTopSize; +} + +inline void BCData::SetTopEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart) +{ + mTopOwner = aOwner; + mTopSize = (aSize > MAX_BORDER_WIDTH) ? MAX_BORDER_WIDTH : aSize; + mTopStart = aStart; +} + +inline PRUint8 BCData::GetCorner(PRUint8& aOwnerSide, + PRPackedBool& aBevel) const +{ + aOwnerSide = mCornerSide; + aBevel = (PRBool)mCornerBevel; + return (PRUint8)mCornerSubSize; +} + +inline void BCData::SetCorner(PRUint8 aSubSize, + PRUint8 aOwnerSide, + PRBool aBevel) +{ + mCornerSubSize = (aSubSize > MAX_CORNER_SUB_WIDTH) ? MAX_CORNER_SUB_WIDTH : aSubSize; + mCornerSide = aOwnerSide; + mCornerBevel = aBevel; +} + +inline PRBool BCData::IsLeftStart() const +{ + return (PRBool)mLeftStart; +} + +inline void BCData::SetLeftStart(PRBool aValue) +{ + mLeftStart = aValue; +} + +inline PRBool BCData::IsTopStart() const +{ + return (PRBool)mTopStart; +} + +inline void BCData::SetTopStart(PRBool aValue) +{ + mTopStart = aValue; +} + #endif diff --git a/mozilla/layout/html/table/src/nsCellMap.cpp b/mozilla/layout/html/table/src/nsCellMap.cpp index 167268997e0..3b6a835cfe8 100644 --- a/mozilla/layout/html/table/src/nsCellMap.cpp +++ b/mozilla/layout/html/table/src/nsCellMap.cpp @@ -60,12 +60,25 @@ CellData::~CellData() MOZ_COUNT_DTOR(CellData); } +BCCellData::BCCellData(nsTableCellFrame* aOrigCell) +:CellData(aOrigCell) +{ + MOZ_COUNT_CTOR(BCCellData); +} + +BCCellData::~BCCellData() +{ + MOZ_COUNT_DTOR(BCCellData); +} + MOZ_DECL_CTOR_COUNTER(nsCellMap) // nsTableCellMap -nsTableCellMap::nsTableCellMap(nsIPresContext* aPresContext, nsTableFrame& aTableFrame) -:mTableFrame(aTableFrame), mFirstMap(nsnull) +nsTableCellMap::nsTableCellMap(nsIPresContext* aPresContext, + nsTableFrame& aTableFrame, + PRBool aBorderCollapse) +:mTableFrame(aTableFrame), mFirstMap(nsnull), mBCInfo(nsnull) { MOZ_COUNT_CTOR(nsTableCellMap); @@ -83,11 +96,17 @@ nsTableCellMap::nsTableCellMap(nsIPresContext* aPresContext, nsTableFrame& aTabl InsertGroupCellMap(*rgFrame, prior); } } + if (aBorderCollapse) { + mBCInfo = new BCInfo(); + } } nsTableCellMap::~nsTableCellMap() { MOZ_COUNT_DTOR(nsTableCellMap); + + PRInt32 rowCount = GetRowCount(); + nsCellMap* cellMap = mFirstMap; while (cellMap) { nsCellMap* next = cellMap->GetNextSibling(); @@ -95,15 +114,89 @@ nsTableCellMap::~nsTableCellMap() cellMap = next; } - PRInt32 colCount = mCols.Count(); + PRInt32 colCount = mCols.Count(); for (PRInt32 colX = 0; colX < colCount; colX++) { nsColInfo* colInfo = (nsColInfo *)mCols.ElementAt(colX); if (colInfo) { delete colInfo; } } + if (mBCInfo) { + DeleteRightBottomBorders(); + delete mBCInfo; + } } +// Get the bcData holding the border segments of the right edge of the table +BCData* +nsTableCellMap::GetRightMostBorder(PRInt32 aRowIndex) +{ + if (!mBCInfo) ABORT1(nsnull); + + BCData* bcData; + + PRInt32 numRows = mBCInfo->mRightBorders.Count(); + if (aRowIndex < numRows) { + bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(aRowIndex); + } + else { + for (PRInt32 rowX = numRows; rowX <= aRowIndex; rowX++) { + bcData = new BCData(); if (!bcData) ABORT1(nsnull); + mBCInfo->mRightBorders.AppendElement(bcData); + } + } + return bcData; +} + +// Get the bcData holding the border segments of the bottom edge of the table +BCData* +nsTableCellMap::GetBottomMostBorder(PRInt32 aColIndex) +{ + if (!mBCInfo) ABORT1(nsnull); + + BCData* bcData; + + PRInt32 numCols = mBCInfo->mBottomBorders.Count(); + if (aColIndex < numCols) { + bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(aColIndex); + } + else { + for (PRInt32 colX = numCols; colX <= aColIndex; colX++) { + bcData = new BCData(); if (!bcData) ABORT1(nsnull); + mBCInfo->mBottomBorders.AppendElement(bcData); + } + } + return bcData; +} + +// delete the borders corresponding to the right and bottom edges of the table +void +nsTableCellMap::DeleteRightBottomBorders() +{ + if (mBCInfo) { + PRInt32 numCols = mBCInfo->mBottomBorders.Count(); + if (numCols > 0) { + for (PRInt32 colX = numCols - 1; colX >= 0; colX--) { + BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX); + if (bcData) { + delete bcData; + } + mBCInfo->mBottomBorders.RemoveElementAt(colX); + } + } + PRUint32 numRows = mBCInfo->mRightBorders.Count(); + if (numRows > 0) { + for (PRInt32 rowX = numRows - 1; rowX >= 0; rowX--) { + BCData* bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(rowX); + if (bcData) { + delete bcData; + } + mBCInfo->mRightBorders.RemoveElementAt(rowX); + } + } + } +} + void nsTableCellMap::InsertGroupCellMap(nsCellMap* aPrevMap, nsCellMap& aNewMap) @@ -126,9 +219,11 @@ void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame& aNewGroup, nsCellMap* newMap = new nsCellMap(aNewGroup); if (newMap) { nsCellMap* prevMap = nsnull; + nsCellMap* lastMap = mFirstMap; if (aPrevGroup) { nsCellMap* map = mFirstMap; while (map) { + lastMap = map; if (map->GetRowGroup() == aPrevGroup) { prevMap = map; break; @@ -137,7 +232,13 @@ void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame& aNewGroup, } } if (!prevMap) { - aPrevGroup = nsnull; + if (aPrevGroup) { + prevMap = lastMap; + aPrevGroup = (prevMap) ? prevMap->GetRowGroup() : nsnull; + } + else { + aPrevGroup = nsnull; + } } InsertGroupCellMap(prevMap, *newMap); } @@ -164,6 +265,16 @@ void nsTableCellMap::RemoveGroupCellMap(nsTableRowGroupFrame* aGroup) } } +nsCellMap* +nsTableCellMap::GetMapFor(nsTableRowGroupFrame& aRowGroup) +{ + for (nsCellMap* map = mFirstMap; map; map = map->GetNextSibling()) { + if (&aRowGroup == map->GetRowGroup()) { + return map; + } + } + return nsnull; +} PRInt32 @@ -241,14 +352,15 @@ nsTableCellMap::GetRowCount() const } CellData* -nsTableCellMap::GetCellAt(PRInt32 aRowIndex, - PRInt32 aColIndex) +nsTableCellMap::GetDataAt(PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan) { PRInt32 rowIndex = aRowIndex; nsCellMap* map = mFirstMap; while (map) { if (map->GetRowCount() > rowIndex) { - return map->GetCellAt(*this, rowIndex, aColIndex); + return map->GetDataAt(*this, rowIndex, aColIndex, aUpdateZeroSpan); } rowIndex -= map->GetRowCount(); map = map->GetNextSibling(); @@ -268,6 +380,12 @@ nsTableCellMap::AddColsAtEnd(PRUint32 aNumCols) if (colInfo) { mCols.AppendElement(colInfo); } + if (mBCInfo) { + BCData* bcData = new BCData(); + if (bcData) { + mBCInfo->mBottomBorders.AppendElement(bcData); + } + } } } @@ -283,6 +401,16 @@ nsTableCellMap::RemoveColsAtEnd() if (colInfo) { if ((colInfo->mNumCellsOrig <= 0) && (colInfo->mNumCellsSpan <= 0)) { mCols.RemoveElementAt(colX); + if (mBCInfo) { + PRInt32 count = mBCInfo->mBottomBorders.Count(); + if (colX < count) { + BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX); + if (bcData) { + delete bcData; + } + mBCInfo->mBottomBorders.RemoveElementAt(colX); + } + } } else break; // only remove until we encounter the 1st valid one } @@ -298,21 +426,44 @@ nsTableCellMap::InsertRows(nsIPresContext* aPresContext, nsTableRowGroupFrame& aParent, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { - NS_ASSERTION((aRows.Count() > 0) && (aFirstRowIndex >= 0), "nsTableCellMap::InsertRows called incorrectly"); + PRInt32 numNewRows = aRows.Count(); + if ((numNewRows <= 0) || (aFirstRowIndex < 0)) ABORT0(); + PRInt32 rowIndex = aFirstRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { - if (cellMap->GetRowGroup() == &aParent) { - cellMap->InsertRows(aPresContext, *this, aRows, rowIndex, aConsiderSpans); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + if (rg == &aParent) { + cellMap->InsertRows(aPresContext, *this, aRows, rowIndex, aConsiderSpans, aDamageArea); + aDamageArea.y = aFirstRowIndex; + aDamageArea.height = PR_MAX(0, GetRowCount() - aFirstRowIndex); //Dump("after InsertRows"); + if (mBCInfo) { + BCData* bcData; + PRInt32 count = mBCInfo->mRightBorders.Count(); + if (aFirstRowIndex < count) { + for (PRInt32 rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) { + bcData = new BCData(); if (!bcData) ABORT0(); + mBCInfo->mRightBorders.InsertElementAt(bcData, rowX); + } + } + else { + GetRightMostBorder(aFirstRowIndex); // this will create missing entries + for (PRInt32 rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) { + bcData = new BCData(); if (!bcData) ABORT0(); + mBCInfo->mRightBorders.AppendElement(bcData); + } + } + } return; } rowIndex -= cellMap->GetRowCount(); cellMap = cellMap->GetNextSibling(); } - + NS_ASSERTION(PR_FALSE, "Attempt to insert row into wrong map."); } @@ -320,13 +471,29 @@ void nsTableCellMap::RemoveRows(nsIPresContext* aPresContext, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { PRInt32 rowIndex = aFirstRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowCount() > rowIndex) { - cellMap->RemoveRows(aPresContext, *this, rowIndex, aNumRowsToRemove, aConsiderSpans); + cellMap->RemoveRows(aPresContext, *this, rowIndex, aNumRowsToRemove, aConsiderSpans, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; + aDamageArea.height = PR_MAX(0, GetRowCount() - aFirstRowIndex); + if (mBCInfo) { + BCData* bcData; + for (PRInt32 rowX = aFirstRowIndex + aNumRowsToRemove - 1; rowX >= aFirstRowIndex; rowX--) { + if (rowX < mBCInfo->mRightBorders.Count()) { + bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(rowX); + if (bcData) { + delete bcData; + } + mBCInfo->mRightBorders.RemoveElementAt(rowX); + } + } + } break; } rowIndex -= cellMap->GetRowCount(); @@ -344,7 +511,7 @@ nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) PRInt32 colIndex = 0; do { - cellData = GetCellAt(aRowIndex, colIndex); + cellData = GetDataAt(aRowIndex, colIndex); if (cellData && cellData->GetCellFrame()) originCount++; colIndex++; @@ -353,10 +520,11 @@ nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) return originCount; } -PRInt32 +CellData* nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, PRInt32 aRowIndex, - PRBool aRebuildIfNecessary) + PRBool aRebuildIfNecessary, + nsRect& aDamageArea) { NS_ASSERTION(&aCellFrame == aCellFrame.GetFirstInFlow(), "invalid call on continuing frame"); nsIFrame* rgFrame = nsnull; @@ -365,12 +533,14 @@ nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, rgFrame->GetParent(&rgFrame); // get the row group if (!rgFrame) return 0; - PRInt32 result = 0; + CellData* result = nsnull; PRInt32 rowIndex = aRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowGroup() == rgFrame) { - result = cellMap->AppendCell(*this, aCellFrame, rowIndex, aRebuildIfNecessary); + result = cellMap->AppendCell(*this, &aCellFrame, rowIndex, aRebuildIfNecessary, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; break; } rowIndex -= cellMap->GetRowCount(); @@ -384,13 +554,17 @@ nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, void nsTableCellMap::InsertCells(nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore) + PRInt32 aColIndexBefore, + nsRect& aDamageArea) { PRInt32 rowIndex = aRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowCount() > rowIndex) { - cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore); + cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; + aDamageArea.width = PR_MAX(0, GetColCount() - aColIndexBefore); break; } rowIndex -= cellMap->GetRowCount(); @@ -402,15 +576,22 @@ nsTableCellMap::InsertCells(nsVoidArray& aCellFrames, void nsTableCellMap::RemoveCell(nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex) + PRInt32 aRowIndex, + nsRect& aDamageArea) { + if (!aCellFrame) ABORT0(); NS_ASSERTION(aCellFrame == (nsTableCellFrame *)aCellFrame->GetFirstInFlow(), "invalid call on continuing frame"); PRInt32 rowIndex = aRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowCount() > rowIndex) { - cellMap->RemoveCell(*this, aCellFrame, rowIndex); + cellMap->RemoveCell(*this, aCellFrame, rowIndex, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; + PRInt32 colIndex; + aCellFrame->GetColIndex(colIndex); + aDamageArea.width = PR_MAX(0, GetColCount() - colIndex); break; } rowIndex -= cellMap->GetRowCount(); @@ -448,7 +629,7 @@ nsTableCellMap::Dump(char* aString) const } nsCellMap* cellMap = mFirstMap; while (cellMap) { - cellMap->Dump(); + cellMap->Dump(nsnull != mBCInfo); cellMap = cellMap->GetNextSibling(); } printf("***** END TABLE CELL MAP DUMP *****\n"); @@ -525,6 +706,237 @@ PRBool nsTableCellMap::ColHasSpanningCells(PRInt32 aColIndex) return PR_FALSE; } +BCData* +nsTableCellMap::GetBCData(PRUint8 aSide, + nsCellMap& aCellMap, + PRUint32 aRowIndex, + PRUint32 aColIndex, + PRBool aIsLowerRight) +{ + if (!mBCInfo || aIsLowerRight) ABORT1(nsnull); + + BCCellData* cellData; + BCData* bcData = nsnull; + + switch(aSide) { + case NS_SIDE_BOTTOM: + aRowIndex++; + case NS_SIDE_TOP: + cellData = (BCCellData*)aCellMap.GetDataAt(*this, aRowIndex, aColIndex, PR_FALSE); + if (cellData) { + bcData = &cellData->mData; + } + else { + NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error"); + // try the next row group + nsCellMap* cellMap = aCellMap.GetNextSibling(); + if (cellMap) { + cellData = (BCCellData*)cellMap->GetDataAt(*this, aRowIndex, aColIndex, PR_FALSE); + if (cellData) { + bcData = &cellData->mData; + } + else { + BCData* bcData = GetBottomMostBorder(aColIndex); + } + } + } + break; + case NS_SIDE_RIGHT: + aColIndex++; + case NS_SIDE_LEFT: + cellData = (BCCellData*)aCellMap.GetDataAt(*this, aRowIndex, aColIndex, PR_FALSE); + if (cellData) { + bcData = &cellData->mData; + } + else { + NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error"); + BCData* bcData = GetRightMostBorder(aRowIndex); + } + break; + } + return bcData; +} + +// store the aSide border segment at coord = (aRowIndex, aColIndex). For top/left, store +// the info at coord. For bottom/left store it at the adjacent location so that it is +// top/left at that location. If the new location is at the right or bottom edge of the +// table, then store it one of the special arrays (right most borders, bottom most borders). +void +nsTableCellMap::SetBCBorderEdge(PRUint8 aSide, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aRowIndex, + PRUint32 aColIndex, + PRUint32 aLength, + BCBorderOwner aOwner, + nscoord aSize, + PRBool aChanged) +{ + if (!mBCInfo) ABORT0(); + //if (aRowIndex >= 80) { + // NS_ASSERTION(PR_FALSE, "hello"); + //} + + BCCellData* cellData; + PRInt32 lastIndex, xIndex, yIndex; + PRInt32 xPos = aColIndex; + PRInt32 yPos = aRowIndex; + PRInt32 rgYPos = aRowIndex - aCellMapStart; + PRBool changed; + + switch(aSide) { + case NS_SIDE_BOTTOM: + rgYPos++; + yPos++; + case NS_SIDE_TOP: + lastIndex = xPos + aLength - 1; + for (xIndex = xPos; xIndex <= lastIndex; xIndex++) { + changed = aChanged && (xIndex == xPos); + BCData* bcData = nsnull; + cellData = (BCCellData*)aCellMap.GetDataAt(*this, rgYPos, xIndex, PR_FALSE); + if (!cellData) { + PRInt32 numRgRows = aCellMap.GetRowCount(); + if (yPos < numRgRows) { // add a dead cell data + nsRect damageArea; + cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + else { + NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error"); + // try the next non empty row group + nsCellMap* cellMap = aCellMap.GetNextSibling(); + while (cellMap && (0 == cellMap->GetRowCount())) { + cellMap = cellMap->GetNextSibling(); + } + if (cellMap) { + cellData = (BCCellData*)cellMap->GetDataAt(*this, 0, xIndex, PR_FALSE); + if (!cellData) { // add a dead cell + nsRect damageArea; + cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea); + } + } + else { // must be at the end of the table + bcData = GetBottomMostBorder(xIndex); + } + } + } + if (!bcData && cellData) { + bcData = &cellData->mData; + } + if (bcData) { + bcData->SetTopEdge(aOwner, aSize, changed); + } + else NS_ASSERTION(PR_FALSE, "program error"); + } + break; + case NS_SIDE_RIGHT: + xPos++; + case NS_SIDE_LEFT: + // since top, bottom borders were set, there should already be a cellData entry + lastIndex = rgYPos + aLength - 1; + for (yIndex = rgYPos; yIndex <= lastIndex; yIndex++) { + changed = aChanged && (yIndex == rgYPos); + cellData = (BCCellData*)aCellMap.GetDataAt(*this, yIndex, xPos, PR_FALSE); + if (cellData) { + cellData->mData.SetLeftEdge(aOwner, aSize, changed); + } + else { + NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error"); + BCData* bcData = GetRightMostBorder(yIndex + aCellMapStart); + if (bcData) { + bcData->SetLeftEdge(aOwner, aSize, changed); + } + else NS_ASSERTION(PR_FALSE, "program error"); + } + } + break; + } +} + +// store corner info (aOwner, aSubSize, aBevel). For aCorner = eTopLeft, store the info at +// (aRowIndex, aColIndex). For eTopRight, store it in the entry to the right where +// it would be top left. For eBottomRight, store it in the entry to the bottom. etc. +void +nsTableCellMap::SetBCBorderCorner(Corner aCorner, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aRowIndex, + PRUint32 aColIndex, + PRUint8 aOwner, + nscoord aSubSize, + PRBool aBevel, + PRBool aIsBottomRight) +{ + if (!mBCInfo) ABORT0(); + + if (aIsBottomRight) { + mBCInfo->mLowerRightCorner.SetCorner(aSubSize, aOwner, aBevel); + return; + } + + PRInt32 xPos = aColIndex; + PRInt32 yPos = aRowIndex; + PRInt32 rgYPos = aRowIndex - aCellMapStart; + + if (eTopRight == aCorner) { + xPos++; + } + else if (eBottomRight == aCorner) { + xPos++; + rgYPos++; + yPos++; + } + else if (eBottomLeft == aCorner) { + rgYPos++; + yPos++; + } + + BCCellData* cellData = nsnull; + BCData* bcData = nsnull; + if (GetColCount() <= xPos) { + NS_ASSERTION(xPos == GetColCount(), "program error"); + if (aIsBottomRight) { // at the bottom right corner + bcData = &mBCInfo->mLowerRightCorner; + } + else { // at the right edge of the table + bcData = GetRightMostBorder(yPos); + } + } + else { + cellData = (BCCellData*)aCellMap.GetDataAt(*this, rgYPos, xPos, PR_FALSE); + if (!cellData) { + PRInt32 numRgRows = aCellMap.GetRowCount(); + if (yPos < numRgRows) { // add a dead cell data + nsRect damageArea; + cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea); + } + else { + // try the next non empty row group + nsCellMap* cellMap = aCellMap.GetNextSibling(); + while (cellMap && (0 == cellMap->GetRowCount())) { + cellMap = cellMap->GetNextSibling(); + } + if (cellMap) { + cellData = (BCCellData*)cellMap->GetDataAt(*this, 0, xPos, PR_FALSE); + if (!cellData) { // add a dead cell + nsRect damageArea; + cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea); + } + } + else { // must be a the bottom of the table + bcData = GetBottomMostBorder(xPos); + } + } + } + } + if (!bcData && cellData) { + bcData = &cellData->mData; + } + if (bcData) { + bcData->SetCorner(aSubSize, aOwner, aBevel); + } + else NS_ASSERTION(PR_FALSE, "program error"); +} + #ifdef DEBUG void nsTableCellMap::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const @@ -540,6 +952,7 @@ void nsTableCellMap::SizeOf(nsISizeOfHandler* aHandler, mCols.SizeOf(aHandler, &voidArraySize); sum += voidArraySize - sizeof(mCols); + // XXX need to so something for mBCInfo *aResult = sum; } #endif @@ -633,7 +1046,8 @@ nsCellMap::InsertRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { PRInt32 numCols = aMap.GetColCount(); @@ -648,7 +1062,7 @@ nsCellMap::InsertRows(nsIPresContext* aPresContext, } if (!aConsiderSpans) { - ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex); + ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex, aDamageArea); return; } @@ -663,10 +1077,10 @@ nsCellMap::InsertRows(nsIPresContext* aPresContext, } if (spansCauseRebuild) { - RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, &aRows); + RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, &aRows, 0, aDamageArea); } else { - ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex); + ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex, aDamageArea); } } @@ -675,7 +1089,8 @@ nsCellMap::RemoveRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { PRInt32 numRows = mRows.Count(); PRInt32 numCols = aMap.GetColCount(); @@ -684,7 +1099,7 @@ nsCellMap::RemoveRows(nsIPresContext* aPresContext, return; } if (!aConsiderSpans) { - ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove); + ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea); return; } PRInt32 endRowIndex = aFirstRowIndex + aNumRowsToRemove - 1; @@ -696,50 +1111,70 @@ nsCellMap::RemoveRows(nsIPresContext* aPresContext, 0, numCols - 1); if (spansCauseRebuild) { - RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, nsnull, aNumRowsToRemove); + RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, nsnull, aNumRowsToRemove, aDamageArea); } else { - ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove); + ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea); } } -PRInt32 +void +SetDamageArea(PRInt32 aXOrigin, + PRInt32 aYOrigin, + PRInt32 aWidth, + PRInt32 aHeight, + nsRect& aDamageArea) +{ + aDamageArea.x = aXOrigin; + aDamageArea.y = aYOrigin; + aDamageArea.width = PR_MAX(1, aWidth); + aDamageArea.height = PR_MAX(1, aHeight); +} + + +CellData* nsCellMap::AppendCell(nsTableCellMap& aMap, - nsTableCellFrame& aCellFrame, + nsTableCellFrame* aCellFrame, PRInt32 aRowIndex, - PRBool aRebuildIfNecessary) + PRBool aRebuildIfNecessary, + nsRect& aDamageArea) { PRInt32 origNumMapRows = mRows.Count(); PRInt32 origNumCols = aMap.GetColCount(); PRBool zeroRowSpan; - PRInt32 rowSpan = GetRowSpanForNewCell(aCellFrame, aRowIndex, zeroRowSpan); + PRInt32 rowSpan = (aCellFrame) ? GetRowSpanForNewCell(*aCellFrame, aRowIndex, zeroRowSpan) : 1; // add new rows if necessary PRInt32 endRowIndex = aRowIndex + rowSpan - 1; if (endRowIndex >= origNumMapRows) { Grow(aMap, 1 + endRowIndex - origNumMapRows); } - // get the first null CellData in the desired row. It may be 1 past the end if there are none + // get the first null or dead CellData in the desired row. It will equal origNumCols if there are none + CellData* origData = nsnull; PRInt32 startColIndex; for (startColIndex = 0; startColIndex < origNumCols; startColIndex++) { - CellData* data = GetMapCellAt(aMap, aRowIndex, startColIndex, PR_TRUE); + CellData* data = GetDataAt(aMap, aRowIndex, startColIndex, PR_TRUE); if (!data) { - break; // we found the col + break; + } + else if (data->IsDead()) { + origData = data; + break; } } + PRBool zeroColSpan; - PRInt32 colSpan = GetColSpanForNewCell(aCellFrame, startColIndex, origNumCols, zeroColSpan); + PRInt32 colSpan = (aCellFrame) ? GetColSpanForNewCell(*aCellFrame, startColIndex, origNumCols, zeroColSpan) : 1; // if the new cell could potentially span into other rows and collide with // originating cells there, we will play it safe and just rebuild the map if (aRebuildIfNecessary && (aRowIndex < mRowCount - 1) && (rowSpan > 1)) { nsAutoVoidArray newCellArray; - newCellArray.AppendElement(&aCellFrame); - RebuildConsideringCells(aMap, &newCellArray, aRowIndex, startColIndex, PR_TRUE); - return startColIndex; + newCellArray.AppendElement(aCellFrame); + RebuildConsideringCells(aMap, &newCellArray, aRowIndex, startColIndex, PR_TRUE, aDamageArea); + return origData; } - mRowCount = PR_MAX(mRowCount, aRowIndex + 1); // add new cols to the table map if necessary @@ -749,19 +1184,29 @@ nsCellMap::AppendCell(nsTableCellMap& aMap, } // Setup CellData for this cell - CellData* origData = new CellData(&aCellFrame); - if (!origData) return startColIndex; - SetMapCellAt(aMap, *origData, aRowIndex, startColIndex, PR_TRUE); + if (origData) { + origData->Init(aCellFrame); + } + else { + origData = (aMap.mBCInfo) ? new BCCellData(aCellFrame) : new CellData(aCellFrame); if (!origData) ABORT1(origData); + SetDataAt(aMap, *origData, aRowIndex, startColIndex, PR_TRUE); + } + + SetDamageArea(startColIndex, aRowIndex, 1 + endColIndex - startColIndex, 1 + endRowIndex - aRowIndex, aDamageArea); + + if (!aCellFrame) { + return origData; + } // initialize the cell frame - aCellFrame.InitCellFrame(startColIndex); + aCellFrame->InitCellFrame(startColIndex); // Create CellData objects for the rows that this cell spans. Set // their mOrigCell to nsnull and their mSpanData to point to data. for (PRInt32 rowX = aRowIndex; rowX <= endRowIndex; rowX++) { for (PRInt32 colX = startColIndex; colX <= endColIndex; colX++) { if ((rowX != aRowIndex) || (colX != startColIndex)) { // skip orig cell data done above - CellData* cellData = GetMapCellAt(aMap, rowX, colX, PR_FALSE); + CellData* cellData = GetDataAt(aMap, rowX, colX, PR_FALSE); if (cellData) { if (cellData->IsOrig()) { NS_ASSERTION(PR_FALSE, "cannot overlap originating cell"); @@ -796,8 +1241,8 @@ nsCellMap::AppendCell(nsTableCellMap& aMap, } } else { - cellData = new CellData(nsnull); - if (!cellData) return startColIndex; + cellData = (aMap.mBCInfo) ? new BCCellData(nsnull) : new CellData(nsnull); + if (!cellData) return origData; if (rowX > aRowIndex) { cellData->SetRowSpanOffset(rowX - aRowIndex); } @@ -811,14 +1256,14 @@ nsCellMap::AppendCell(nsTableCellMap& aMap, cellData->SetZeroColSpan(PR_TRUE); } // only count the 1st spanned col of a zero col span - SetMapCellAt(aMap, *cellData, rowX, colX, (colX == startColIndex + 1)); + SetDataAt(aMap, *cellData, rowX, colX, (colX == startColIndex + 1)); } } } } - //printf("appended cell=%p row=%d \n", &aCellFrame, aRowIndex); + //printf("appended cell=%p row=%d \n", aCellFrame, aRowIndex); //aMap.Dump(); - return startColIndex; + return origData; } PRBool nsCellMap::CellsSpanOut(nsIPresContext* aPresContext, @@ -832,7 +1277,7 @@ PRBool nsCellMap::CellsSpanOut(nsIPresContext* aPresContext, while (cellFrame) { nsIAtom* frameType; cellFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableCellFrame == frameType) { + if (IS_TABLE_CELL(frameType)) { PRBool zeroSpan; PRInt32 rowSpan = GetRowSpanForNewCell((nsTableCellFrame &)*cellFrame, rowX, zeroSpan); if (rowX + rowSpan > numNewRows) { @@ -858,13 +1303,13 @@ PRBool nsCellMap::CellsSpanInOrOut(nsTableCellMap& aMap, for (PRInt32 colX = aStartColIndex; colX <= aEndColIndex; colX++) { CellData* cellData; if (aStartRowIndex > 0) { - cellData = GetMapCellAt(aMap, aStartRowIndex, colX, PR_TRUE); + cellData = GetDataAt(aMap, aStartRowIndex, colX, PR_TRUE); if (cellData && (cellData->IsRowSpan())) { return PR_TRUE; // there is a row span into the region } } if (aEndRowIndex < mRowCount - 1) { - cellData = GetMapCellAt(aMap, aEndRowIndex + 1, colX, PR_TRUE); + cellData = GetDataAt(aMap, aEndRowIndex + 1, colX, PR_TRUE); if ((cellData) && (cellData->IsRowSpan())) { return PR_TRUE; // there is a row span out of the region } @@ -872,7 +1317,7 @@ PRBool nsCellMap::CellsSpanInOrOut(nsTableCellMap& aMap, } if (aStartColIndex > 0) { for (PRInt32 rowX = aStartRowIndex; rowX <= aEndRowIndex; rowX++) { - CellData* cellData = GetMapCellAt(aMap, rowX, aStartColIndex, PR_TRUE); + CellData* cellData = GetDataAt(aMap, rowX, aStartColIndex, PR_TRUE); if (cellData && (cellData->IsColSpan())) { return PR_TRUE; // there is a col span into the region } @@ -891,7 +1336,8 @@ PRBool nsCellMap::CellsSpanInOrOut(nsTableCellMap& aMap, void nsCellMap::InsertCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore) + PRInt32 aColIndexBefore, + nsRect& aDamageArea) { if (aCellFrames.Count() == 0) return; PRInt32 numCols = aMap.GetColCount(); @@ -903,7 +1349,7 @@ void nsCellMap::InsertCells(nsTableCellMap& aMap, // get the starting col index of the 1st new cells PRInt32 startColIndex; for (startColIndex = aColIndexBefore + 1; startColIndex < numCols; startColIndex++) { - CellData* data = GetMapCellAt(aMap, aRowIndex, startColIndex, PR_TRUE); + CellData* data = GetDataAt(aMap, aRowIndex, startColIndex, PR_TRUE); if (!data || data->IsOrig()) { // stop unless it is a span break; } @@ -942,10 +1388,10 @@ void nsCellMap::InsertCells(nsTableCellMap& aMap, } if (spansCauseRebuild) { - RebuildConsideringCells(aMap, &aCellFrames, aRowIndex, startColIndex, PR_TRUE); + RebuildConsideringCells(aMap, &aCellFrames, aRowIndex, startColIndex, PR_TRUE, aDamageArea); } else { - ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, zeroRowSpan); + ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, zeroRowSpan, aDamageArea); } } @@ -953,7 +1399,8 @@ void nsCellMap::ExpandWithRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRowFrames, - PRInt32 aStartRowIndexIn) + PRInt32 aStartRowIndexIn, + nsRect& aDamageArea) { PRInt32 startRowIndex = (aStartRowIndexIn >= 0) ? aStartRowIndexIn : 0; PRInt32 numNewRows = aRowFrames.Count(); @@ -974,22 +1421,25 @@ nsCellMap::ExpandWithRows(nsIPresContext* aPresContext, while (cFrame) { nsIAtom* cFrameType; cFrame->GetFrameType(&cFrameType); - if (nsLayoutAtoms::tableCellFrame == cFrameType) { - AppendCell(aMap, (nsTableCellFrame &)*cFrame, rowX, PR_FALSE); + if (IS_TABLE_CELL(cFrameType)) { + AppendCell(aMap, (nsTableCellFrame *)cFrame, rowX, PR_FALSE, aDamageArea); } NS_IF_RELEASE(cFrameType); cFrame->GetNextSibling(&cFrame); } newRowIndex++; } + + SetDamageArea(0, startRowIndex, aMap.GetColCount(), 1 + endRowIndex - startRowIndex, aDamageArea); } void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, - PRInt32 aRowSpan, - PRBool aRowSpanIsZero) + PRInt32 aRowSpan, // same for all cells + PRBool aRowSpanIsZero, + nsRect& aDamageArea) { PRInt32 endRowIndex = aRowIndex + aRowSpan - 1; PRInt32 startColIndex = aColIndex; @@ -1000,7 +1450,7 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, // add cellData entries for the space taken up by the new cells for (PRInt32 cellX = 0; cellX < numCells; cellX++) { nsTableCellFrame* cellFrame = (nsTableCellFrame*) aCellFrames.ElementAt(cellX); - CellData* origData = new CellData(cellFrame); // the originating cell + CellData* origData = (aMap.mBCInfo) ? new BCCellData(cellFrame) : new CellData(cellFrame); // the originating cell if (!origData) return; // set the starting and ending col index for the new cell @@ -1022,7 +1472,7 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, row->InsertElementAt(nsnull, colX); CellData* data = origData; if ((rowX != aRowIndex) || (colX != startColIndex)) { - data = new CellData(nsnull); + data = (aMap.mBCInfo) ? new BCCellData(nsnull) : new CellData(nsnull); if (!data) return; if (rowX > aRowIndex) { data->SetRowSpanOffset(rowX - aRowIndex); @@ -1038,11 +1488,13 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, } } // only count 1st spanned col of colspan=0 - SetMapCellAt(aMap, *data, rowX, colX, (colX == aColIndex + 1)); + SetDataAt(aMap, *data, rowX, colX, (colX == aColIndex + 1)); } } cellFrame->InitCellFrame(startColIndex); } + PRInt32 damageHeight = (aRowSpanIsZero) ? aMap.GetColCount() - aRowIndex : aRowSpan; + SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, damageHeight, aDamageArea); PRInt32 rowX; @@ -1090,7 +1542,8 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap, PRInt32 aStartRowIndex, - PRInt32 aNumRowsToRemove) + PRInt32 aNumRowsToRemove, + nsRect& aDamageArea) { PRInt32 endRowIndex = aStartRowIndex + aNumRowsToRemove - 1; PRInt32 colCount = aMap.GetColCount(); @@ -1132,6 +1585,8 @@ void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap, mRowCount--; } aMap.RemoveColsAtEnd(); + + SetDamageArea(0, aStartRowIndex, aMap.GetColCount(), 0, aDamageArea); } PRInt32 nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, @@ -1162,15 +1617,15 @@ PRInt32 nsCellMap::GetEffectiveColSpan(nsTableCellMap& aMap, PRInt32 colX; CellData* data; PRInt32 maxCols = numColsInTable; - PRBool hitOverlap = PR_FALSE; + PRBool hitOverlap = PR_FALSE; // XXX this is not ever being set to PR_TRUE for (colX = aColIndex + 1; colX < maxCols; colX++) { - data = GetMapCellAt(aMap, aRowIndex, colX, PR_TRUE); + data = GetDataAt(aMap, aRowIndex, colX, PR_TRUE); if (data) { // for an overlapping situation get the colspan from the originating cell and // use that as the max number of cols to iterate. Since this is rare, only // pay the price of looking up the cell's colspan here. if (!hitOverlap && data->IsOverlap()) { - CellData* origData = GetMapCellAt(aMap, aRowIndex, aColIndex, PR_TRUE); + CellData* origData = GetDataAt(aMap, aRowIndex, aColIndex, PR_TRUE); if (origData->IsOrig()) { nsTableCellFrame* cellFrame = origData->GetCellFrame(); if (cellFrame) { @@ -1223,7 +1678,7 @@ PRInt32 nsCellMap::GetRowSpan(nsTableCellMap& aMap, PRInt32 rowCount = (aGetEffective) ? mRowCount : mRows.Count(); PRInt32 rowX; for (rowX = aRowIndex + 1; rowX < rowCount; rowX++) { - CellData* data = GetMapCellAt(aMap, rowX, aColIndex, PR_TRUE); + CellData* data = GetDataAt(aMap, rowX, aColIndex, PR_TRUE); if (data) { if (data->IsRowSpan()) { rowSpan++; @@ -1246,7 +1701,8 @@ PRInt32 nsCellMap::GetRowSpan(nsTableCellMap& aMap, void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, nsTableCellFrame& aCellFrame, PRInt32 aRowIndex, - PRInt32 aColIndex) + PRInt32 aColIndex, + nsRect& aDamageArea) { PRInt32 colX, rowX; @@ -1258,6 +1714,8 @@ void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, PRInt32 endRowIndex = aRowIndex + rowSpan - 1; PRInt32 endColIndex = aColIndex + colSpan - 1; + SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, 1 + endRowIndex - aRowIndex, aDamageArea); + // adjust the col counts due to the deleted cell before removing it for (colX = aColIndex; colX <= endColIndex; colX++) { nsColInfo* colInfo = aMap.GetColInfoAt(colX); @@ -1317,25 +1775,13 @@ void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, aMap.RemoveColsAtEnd(); } -void -nsCellMap::RemoveCol(PRInt32 aColIndex) -{ - PRInt32 numMapRows = mRows.Count(); - // remove the col from each of the rows - for (PRInt32 rowX = 0; rowX < numMapRows; rowX++) { - nsVoidArray* row = (nsVoidArray *)mRows.ElementAt(rowX); - CellData* data = (CellData*) row->ElementAt(aColIndex); - row->RemoveElementAt(aColIndex); - delete data; - } -} - void nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aStartRowIndex, nsVoidArray* aRowsToInsert, - PRBool aNumRowsToRemove) + PRBool aNumRowsToRemove, + nsRect& aDamageArea) { // copy the old cell map into a new array PRInt32 numOrigRows = mRows.Count(); @@ -1352,6 +1798,7 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, nsColInfo* colInfo = aMap.GetColInfoAt(colX); colInfo->mNumCellsOrig = 0; } + mRows.Clear(); mRowCount = 0; if (aRowsToInsert) { @@ -1366,7 +1813,7 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, // put in the original cell from the cell map CellData* data = (CellData*) row->ElementAt(colX); if (data && data->IsOrig()) { - AppendCell(aMap, *data->GetCellFrame(), rowX, PR_FALSE); + AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea); } } } @@ -1382,8 +1829,8 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, while (cFrame) { nsIAtom* cFrameType; cFrame->GetFrameType(&cFrameType); - if (nsLayoutAtoms::tableCellFrame == cFrameType) { - AppendCell(aMap, (nsTableCellFrame &)*cFrame, rowX, PR_FALSE); + if (IS_TABLE_CELL(cFrameType)) { + AppendCell(aMap, (nsTableCellFrame *)cFrame, rowX, PR_FALSE, aDamageArea); } NS_IF_RELEASE(cFrameType); cFrame->GetNextSibling(&cFrame); @@ -1405,7 +1852,7 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, // put in the original cell from the cell map CellData* data = (CellData*) row->ElementAt(colX); if (data && data->IsOrig()) { - AppendCell(aMap, *data->GetCellFrame(), rowX, PR_FALSE); + AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea); } } rowX++; @@ -1422,13 +1869,16 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, delete row; } delete [] origRows; + + SetDamageArea(0, 0, aMap.GetColCount(), GetRowCount(), aDamageArea); } void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, nsVoidArray* aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, - PRBool aInsert) + PRBool aInsert, + nsRect& aDamageArea) { // copy the old cell map into a new array PRInt32 mRowCountOrig = mRowCount; @@ -1441,6 +1891,7 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, nsVoidArray* row = (nsVoidArray *)mRows.ElementAt(rowX); origRows[rowX] = row; } + // reinitialize data members mRows.Clear(); mRowCount = 0; @@ -1457,7 +1908,7 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) { nsTableCellFrame* cell = (nsTableCellFrame*)aCellFrames->ElementAt(cellX); if (cell) { - AppendCell(aMap, *cell, rowX, PR_FALSE); + AppendCell(aMap, cell, rowX, PR_FALSE, aDamageArea); } } } @@ -1468,7 +1919,7 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, // put in the original cell from the cell map CellData* data = (CellData*) row->ElementAt(colX); if (data && data->IsOrig()) { - AppendCell(aMap, *data->GetCellFrame(), rowX, PR_FALSE); + AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea); } } } @@ -1490,11 +1941,14 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, delete row; } delete [] origRows; + + SetDamageArea(0, 0, aMap.GetColCount(), GetRowCount(), aDamageArea); } void nsCellMap::RemoveCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex) + PRInt32 aRowIndex, + nsRect& aDamageArea) { PRInt32 numRows = mRows.Count(); if ((aRowIndex < 0) || (aRowIndex >= numRows)) { @@ -1506,7 +1960,7 @@ void nsCellMap::RemoveCell(nsTableCellMap& aMap, // get the starting col index of the cell to remove PRInt32 startColIndex; for (startColIndex = 0; startColIndex < numCols; startColIndex++) { - CellData* data = GetMapCellAt(aMap, aRowIndex, startColIndex, PR_FALSE); + CellData* data = GetDataAt(aMap, aRowIndex, startColIndex, PR_FALSE); if (data && (data->IsOrig()) && (aCellFrame == data->GetCellFrame())) { break; // we found the col index } @@ -1523,15 +1977,15 @@ void nsCellMap::RemoveCell(nsTableCellMap& aMap, // no extraneous cols at the end. The same is true for removing rows. if (spansCauseRebuild) { - RebuildConsideringCells(aMap, nsnull, aRowIndex, startColIndex, PR_FALSE); + RebuildConsideringCells(aMap, nsnull, aRowIndex, startColIndex, PR_FALSE, aDamageArea); } else { - ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex); + ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex, aDamageArea); } } #ifdef NS_DEBUG -void nsCellMap::Dump() const +void nsCellMap::Dump(PRBool aIsBorderCollapse) const { printf("\n ***** START GROUP CELL MAP DUMP ***** %p\n", this); PRInt32 mapRowCount = mRows.Count(); @@ -1566,6 +2020,33 @@ void nsCellMap::Dump() const printf("---- "); } } + if (aIsBorderCollapse) { + nscoord size; + BCBorderOwner owner; + PRUint8 side; + PRBool segStart; + PRPackedBool bevel; + for (PRInt32 i = 0; i <= 2; i++) { + printf("\n "); + for (colIndex = 0; colIndex < colCount; colIndex++) { + BCCellData* cd = (BCCellData *)row->ElementAt(colIndex); + if (cd) { + if (0 == i) { + size = cd->mData.GetTopEdge(owner, segStart); + printf("t=%d%d%d ", size, owner, segStart); + } + else if (1 == i) { + size = cd->mData.GetLeftEdge(owner, segStart); + printf("l=%d%d%d ", size, owner, segStart); + } + else { + size = cd->mData.GetCorner(side, bevel); + printf("c=%d%d%d ", size, side, bevel); + } + } + } + } + } printf("\n"); } @@ -1614,7 +2095,7 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, PRInt32 aColIndex) { PRInt32 numColsInTable = aMap.GetColCount(); - CellData* data = GetMapCellAt(aMap, aRowIndex, aColIndex, PR_FALSE); + CellData* data = GetDataAt(aMap, aRowIndex, aColIndex, PR_FALSE); if (!data) return; nsTableCellFrame* cell = (data->IsOrig()) ? data->GetCellFrame() : nsnull; @@ -1640,7 +2121,7 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, PRBool cellsOrig = PR_FALSE; if (colX >= aColIndex + MIN_NUM_COLS_FOR_ZERO_COLSPAN - 1) { for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { - CellData* cellData = GetMapCellAt(aMap, rowX, colX, PR_FALSE); + CellData* cellData = GetDataAt(aMap, rowX, colX, PR_FALSE); if (cellData && cellData->IsOrig()) { cellsOrig = PR_TRUE; break; // there are cells in this col, so don't consider it @@ -1650,9 +2131,9 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, if (cellsOrig) break; for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { if ((colX > aColIndex) || (rowX > aRowIndex)) { - CellData* oldData = GetMapCellAt(aMap, rowX, colX, PR_FALSE); + CellData* oldData = GetDataAt(aMap, rowX, colX, PR_FALSE); if (!oldData) { - CellData* newData = new CellData(nsnull); + CellData* newData = (aMap.mBCInfo) ? new BCCellData(nsnull) : new CellData(nsnull); if (!newData) return; if (colX > aColIndex) { newData->SetColSpanOffset(colX - aColIndex); @@ -1663,7 +2144,7 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, newData->SetZeroRowSpan(PR_TRUE); } // colspan=0 is only counted as spanning the 1st col to the right of its origin - SetMapCellAt(aMap, *newData, rowX, colX, (colX == aColIndex + 1)); + SetDataAt(aMap, *newData, rowX, colX, (colX == aColIndex + 1)); } } } @@ -1671,10 +2152,10 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, } CellData* -nsCellMap::GetMapCellAt(nsTableCellMap& aMap, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aUpdateZeroSpan) +nsCellMap::GetDataAt(nsTableCellMap& aMap, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan) { PRInt32 numColsInTable = aMap.GetColCount(); if ((aMapRowIndex < 0) || (aMapRowIndex >= mRows.Count())) { @@ -1714,7 +2195,7 @@ nsCellMap::GetMapCellAt(nsTableCellMap& aMap, PRInt32 prevColX = aColIndex - 1; // find the last non null data in the same row for ( ; prevColX > 0; prevColX--) { - CellData* prevData = GetMapCellAt(aMap, aMapRowIndex, prevColX, PR_FALSE); + CellData* prevData = GetDataAt(aMap, aMapRowIndex, prevColX, PR_FALSE); if (prevData) { if (prevData->IsZeroColSpan()) { PRInt32 colIndex = prevColX - prevData->GetColSpanOffset(); @@ -1729,29 +2210,18 @@ nsCellMap::GetMapCellAt(nsTableCellMap& aMap, // if zero span adjustments were made the data may be available now if (!data && didZeroExpand) { - data = GetMapCellAt(aMap, aMapRowIndex, aColIndex, PR_FALSE); + data = GetDataAt(aMap, aMapRowIndex, aColIndex, PR_FALSE); } } return data; } -CellData* -nsCellMap::GetCellAt(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex) -{ - if ((0 > aRowIndex) || (aRowIndex >= mRowCount)) { - return nsnull; - } - return GetMapCellAt(aMap, aRowIndex, aColIndex, PR_TRUE); -} - // only called if the cell at aMapRowIndex, aColIndex is null -void nsCellMap::SetMapCellAt(nsTableCellMap& aMap, - CellData& aNewCell, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aCountZeroSpanAsSpan) +void nsCellMap::SetDataAt(nsTableCellMap& aMap, + CellData& aNewCell, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aCountZeroSpanAsSpan) { nsVoidArray* row = (nsVoidArray *)(mRows.SafeElementAt(aMapRowIndex)); if (row) { @@ -1777,9 +2247,9 @@ void nsCellMap::SetMapCellAt(nsTableCellMap& aMap, colInfo->mNumCellsSpan++; } } - else NS_ASSERTION(PR_FALSE, "SetMapCellAt called with col index > table map num cols"); + else NS_ASSERTION(PR_FALSE, "SetDataAt called with col index > table map num cols"); } - else NS_ASSERTION(PR_FALSE, "SetMapCellAt called with row index > num rows"); + else NS_ASSERTION(PR_FALSE, "SetDataAt called with row index > num rows"); } nsTableCellFrame* @@ -1792,7 +2262,7 @@ nsCellMap::GetCellInfoAt(nsTableCellMap& aMap, if (aOriginates) { *aOriginates = PR_FALSE; } - CellData* data = GetCellAt(aMap, aRowX, aColX); + CellData* data = GetDataAt(aMap, aRowX, aColX, PR_TRUE); nsTableCellFrame* cellFrame = nsnull; if (data) { if (data->IsOrig()) { @@ -1824,7 +2294,7 @@ PRBool nsCellMap::RowIsSpannedInto(nsTableCellMap& aMap, return PR_FALSE; } for (PRInt32 colIndex = 0; colIndex < numColsInTable; colIndex++) { - CellData* cd = GetCellAt(aMap, aRowIndex, colIndex); + CellData* cd = GetDataAt(aMap, aRowIndex, colIndex, PR_TRUE); if (cd) { // there's really a cell at (aRowIndex, colIndex) if (cd->IsSpan()) { // the cell at (aRowIndex, colIndex) is the result of a span if (cd->IsRowSpan() && GetCellFrame(aRowIndex, colIndex, *cd, PR_TRUE)) { // XXX why the last check @@ -1846,9 +2316,9 @@ PRBool nsCellMap::RowHasSpanningCells(nsTableCellMap& aMap, if (aRowIndex != mRowCount - 1) { // aRowIndex is not the last row, so we check the next row after aRowIndex for spanners for (PRInt32 colIndex = 0; colIndex < numColsInTable; colIndex++) { - CellData* cd = GetCellAt(aMap, aRowIndex, colIndex); + CellData* cd = GetDataAt(aMap, aRowIndex, colIndex, PR_TRUE); if (cd && (cd->IsOrig())) { // cell originates - CellData* cd2 = GetCellAt(aMap, aRowIndex + 1, colIndex); + CellData* cd2 = GetDataAt(aMap, aRowIndex + 1, colIndex, PR_TRUE); if (cd2 && cd2->IsRowSpan()) { // cd2 is spanned by a row if (cd->GetCellFrame() == GetCellFrame(aRowIndex + 1, colIndex, *cd2, PR_TRUE)) { return PR_TRUE; @@ -1869,9 +2339,9 @@ PRBool nsCellMap::ColHasSpanningCells(nsTableCellMap& aMap, return PR_FALSE; for (PRInt32 rowIndex = 0; rowIndex < mRowCount; rowIndex++) { - CellData* cd = GetCellAt(aMap, rowIndex, aColIndex); + CellData* cd = GetDataAt(aMap, rowIndex, aColIndex, PR_TRUE); if (cd && (cd->IsOrig())) { // cell originates - CellData* cd2 = GetCellAt(aMap, rowIndex + 1, aColIndex); + CellData* cd2 = GetDataAt(aMap, rowIndex + 1, aColIndex, PR_TRUE); if (cd2 && cd2->IsColSpan()) { // cd2 is spanned by a col if (cd->GetCellFrame() == GetCellFrame(rowIndex + 1, aColIndex, *cd2, PR_FALSE)) { return PR_TRUE; diff --git a/mozilla/layout/html/table/src/nsCellMap.h b/mozilla/layout/html/table/src/nsCellMap.h index 89211aa5f99..4b1b6df3b7f 100644 --- a/mozilla/layout/html/table/src/nsCellMap.h +++ b/mozilla/layout/html/table/src/nsCellMap.h @@ -40,6 +40,8 @@ #include "nscore.h" #include "celldata.h" #include "nsVoidArray.h" +#include "nsRect.h" + class nsTableColFrame; class nsTableCellFrame; class nsIPresContext; @@ -58,10 +60,27 @@ struct nsColInfo PRInt32 aNumCellsSpan); }; +enum Corner +{ + eTopLeft = 0, + eTopRight = 1, + eBottomRight = 2, + eBottomLeft = 3 +}; + +struct BCInfo +{ + nsVoidArray mRightBorders; + nsVoidArray mBottomBorders; + BCData mLowerRightCorner; +}; + class nsTableCellMap { public: - nsTableCellMap(nsIPresContext* aPresContext, nsTableFrame& aTableFrame); + nsTableCellMap(nsIPresContext* aPresContext, + nsTableFrame& aTableFrame, + PRBool aBorderCollapse); /** destructor * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED @@ -69,43 +88,52 @@ public: ~nsTableCellMap(); void RemoveGroupCellMap(nsTableRowGroupFrame* aRowGroup); + void InsertGroupCellMap(nsTableRowGroupFrame& aNewRowGroup, nsTableRowGroupFrame*& aPrevRowGroup); + nsCellMap* GetMapFor(nsTableRowGroupFrame& aRowGroup); + nsTableCellFrame* GetCellFrame(PRInt32 aRowIndex, PRInt32 aColIndex, CellData& aData, PRBool aUseRowIfOverlap) const; /** return the CellData for the cell at (aTableRowIndex, aTableColIndex) */ - CellData* GetCellAt(PRInt32 aRowIndex, - PRInt32 aColIndex); + CellData* GetDataAt(PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan = PR_TRUE); nsColInfo* GetColInfoAt(PRInt32 aColIndex); /** append the cellFrame at the end of the row at aRowIndex and return the col index */ - PRInt32 AppendCell(nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex, - PRBool aRebuildIfNecessary); + CellData* AppendCell(nsTableCellFrame& aCellFrame, + PRInt32 aRowIndex, + PRBool aRebuildIfNecessary, + nsRect& aDamageArea); void InsertCells(nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore); + PRInt32 aColIndexBefore, + nsRect& aDamageArea); void RemoveCell(nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex); + PRInt32 aRowIndex, + nsRect& aDamageArea); void InsertRows(nsIPresContext* aPresContext, nsTableRowGroupFrame& aRowGroup, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); void RemoveRows(nsIPresContext* aPresContext, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex); PRInt32 GetNumCellsOriginatingInCol(PRInt32 aColIndex) const; @@ -134,6 +162,32 @@ public: PRBool ColIsSpannedInto(PRInt32 aColIndex); PRBool ColHasSpanningCells(PRInt32 aColIndex); + BCData* GetBCData(PRUint8 aSide, + nsCellMap& aCellMap, + PRUint32 aYPos, + PRUint32 aXPos, + PRBool aIsLowerRight = PR_FALSE); + + void SetBCBorderEdge(PRUint8 aEdge, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aYPos, + PRUint32 aXPos, + PRUint32 aLength, + BCBorderOwner aOwner, + nscoord aSize, + PRBool aChanged); + + void SetBCBorderCorner(Corner aCorner, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aYPos, + PRUint32 aXPos, + PRUint8 aOwner, + nscoord aSubSize, + PRBool aBevel, + PRBool aIsBottomRight = PR_FALSE); + /** dump a representation of the cell map to stdout for debugging */ #ifdef NS_DEBUG void Dump(char* aString = nsnull) const; @@ -144,13 +198,21 @@ public: #endif protected: + BCData* GetRightMostBorder(PRInt32 aRowIndex); + BCData* GetBottomMostBorder(PRInt32 aColIndex); + friend class nsCellMap; + friend class BCMapCellIterator; + friend class BCMapBorderIterator; void InsertGroupCellMap(nsCellMap* aPrevMap, nsCellMap& aNewMap); + void DeleteRightBottomBorders(); - nsTableFrame& mTableFrame; + nsTableFrame& mTableFrame; nsAutoVoidArray mCols; - nsCellMap* mFirstMap; + nsCellMap* mFirstMap; + // border collapsing info + BCInfo* mBCInfo; }; /** nsCellMap is a support class for nsTablePart. @@ -190,40 +252,38 @@ public: CellData& aData, PRBool aUseRowSpanIfOverlap) const; - /** return the CellData for the cell at (aTableRowIndex, aTableColIndex) */ - CellData* GetCellAt(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex); - /** append the cellFrame at the end of the row at aRowIndex and return the col index */ - PRInt32 AppendCell(nsTableCellMap& aMap, - nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex, - PRBool aRebuildIfNecessary); + CellData* AppendCell(nsTableCellMap& aMap, + nsTableCellFrame* aCellFrame, + PRInt32 aRowIndex, + PRBool aRebuildIfNecessary, + nsRect& aDamageArea); void InsertCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore); + PRInt32 aColIndexBefore, + nsRect& aDamageArea); void RemoveCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex); - - void RemoveCol(PRInt32 aColIndex); + PRInt32 aRowIndex, + nsRect& aDamageArea); void InsertRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); void RemoveRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const; PRInt32 GetNumCellsOriginatingInCol(PRInt32 aColIndex) const; @@ -246,9 +306,20 @@ public: PRBool ColHasSpanningCells(nsTableCellMap& aMap, PRInt32 aColIndex); + PRInt32 GetRowSpan(nsTableCellMap& aMap, + PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool aGetEffective, + PRBool& aIsZeroRowSpan); + + PRInt32 GetEffectiveColSpan(nsTableCellMap& aMap, + PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool& aIsZeroColSpan); + /** dump a representation of the cell map to stdout for debugging */ #ifdef NS_DEBUG - void Dump() const; + void Dump(PRBool aIsBorderCollapse) const; #endif #ifdef DEBUG @@ -257,6 +328,9 @@ public: protected: friend class nsTableCellMap; + friend class BCMapCellIterator; + friend class BCMapBorderIterator; + friend class nsTableFrame; PRBool Grow(nsTableCellMap& aMap, PRInt32 aNumRows, @@ -266,51 +340,57 @@ protected: PRInt32 aNumCols); /** assign aCellData to the cell at (aRow,aColumn) */ - void SetMapCellAt(nsTableCellMap& aMap, - CellData& aCellData, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aCountZeroSpanAsSpan); + void SetDataAt(nsTableCellMap& aMap, + CellData& aCellData, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aCountZeroSpanAsSpan); - CellData* GetMapCellAt(nsTableCellMap& aMap, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aUpdateZeroSpan); + CellData* GetDataAt(nsTableCellMap& aMap, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan); PRInt32 GetNumCellsIn(PRInt32 aColIndex) const; void ExpandWithRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRowFrames, - PRInt32 aStartRowIndex); + PRInt32 aStartRowIndex, + nsRect& aDamageArea); void ExpandWithCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, PRInt32 aRowSpan, - PRBool aRowSpanIsZero); + PRBool aRowSpanIsZero, + nsRect& aDamageArea); void ShrinkWithoutRows(nsTableCellMap& aMap, PRInt32 aFirstRowIndex, - PRInt32 aNumRowsToRemove); + PRInt32 aNumRowsToRemove, + nsRect& aDamageArea); void ShrinkWithoutCell(nsTableCellMap& aMap, nsTableCellFrame& aCellFrame, PRInt32 aRowIndex, - PRInt32 aColIndex); + PRInt32 aColIndex, + nsRect& aDamageArea); void RebuildConsideringRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aStartRowIndex, nsVoidArray* aRowsToInsert, - PRInt32 aNumRowsToRemove = 0); + PRInt32 aNumRowsToRemove, + nsRect& aDamageArea); void RebuildConsideringCells(nsTableCellMap& aMap, nsVoidArray* aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, - PRBool aInsert); + PRBool aInsert, + nsRect& aDamageArea); PRBool CellsSpanOut(nsIPresContext* aPresContext, nsVoidArray& aNewRows); @@ -327,21 +407,10 @@ protected: PRBool CreateEmptyRow(PRInt32 aRowIndex, PRInt32 aNumCols); - PRInt32 GetRowSpan(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex, - PRBool aGetEffective, - PRBool& aIsZeroRowSpan); - PRInt32 GetRowSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, PRInt32 aRowIndex, PRBool& aIsZeroRowSpan); - PRInt32 GetEffectiveColSpan(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex, - PRBool& aIsZeroColSpan); - PRInt32 GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, PRInt32 aColIndex, PRInt32 aNumColsInTable, diff --git a/mozilla/layout/html/table/src/nsITableLayoutStrategy.h b/mozilla/layout/html/table/src/nsITableLayoutStrategy.h index 0349e3924d8..bae856d64b4 100644 --- a/mozilla/layout/html/table/src/nsITableLayoutStrategy.h +++ b/mozilla/layout/html/table/src/nsITableLayoutStrategy.h @@ -76,7 +76,8 @@ public: * @param aPixelToTwips - the number of twips in a pixel. * @return - the basis for percent calculations */ - virtual nscoord CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState, + virtual nscoord CalcPctAdjTableWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidth, float aPixelToTwips)=0; diff --git a/mozilla/layout/html/table/src/nsTableCellFrame.cpp b/mozilla/layout/html/table/src/nsTableCellFrame.cpp index 2c0e87b11de..06ca717b3bd 100644 --- a/mozilla/layout/html/table/src/nsTableCellFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableCellFrame.cpp @@ -100,7 +100,7 @@ nsTableCellFrame::GetNextCell() const while (childFrame) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { return (nsTableCellFrame*)childFrame; } childFrame->GetNextSibling(&childFrame); @@ -439,7 +439,7 @@ nsTableCellFrame::Paint(nsIPresContext* aPresContext, if ((NS_SUCCEEDED(rv)) && tableFrame) { const nsStyleTableBorder* tableStyle; tableFrame->GetStyleData(eStyleStruct_TableBorder, ((const nsStyleStruct *&)tableStyle)); - if (NS_STYLE_BORDER_SEPARATE == tableFrame->GetBorderCollapseStyle()) { + if (!tableFrame->IsBorderCollapse()) { nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, aDirtyRect, rect, *myBorder, mStyleContext, skipSides); } @@ -582,10 +582,9 @@ void nsTableCellFrame::VerticallyAlignChild(nsIPresContext* aPresContex const nsStyleTextReset* textStyle = (const nsStyleTextReset*)mStyleContext->GetStyleData(eStyleStruct_TextReset); /* XXX: remove tableFrame when border-collapse inherits */ - nsTableFrame* tableFrame = nsnull; - (void) nsTableFrame::GetTableFrame(this, tableFrame); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); nsMargin borderPadding; - GetCellBorder (borderPadding, tableFrame); + GetBorderWidth (p2t, borderPadding); nsMargin padding = nsTableFrame::GetPadding(aReflowState, this); borderPadding += padding; @@ -822,13 +821,13 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, PRBool contentEmptyBeforeReflow = GetContentEmpty(); /* XXX: remove tableFrame when border-collapse inherits */ - nsTableFrame* tableFrame=nsnull; - rv = nsTableFrame::GetTableFrame(this, tableFrame); + nsTableFrame* tableFrame = nsnull; + rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(NS_ERROR_NULL_POINTER); nsTableFrame* tableFrameFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); nsMargin borderPadding = aReflowState.mComputedPadding; nsMargin border; - GetCellBorder(border, tableFrame); + GetBorderWidth(p2t, border); if ((NS_UNCONSTRAINEDSIZE == availSize.width) || !contentEmptyBeforeReflow) { borderPadding += border; } @@ -1130,51 +1129,6 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, return NS_OK; } -/** - * - * Update the border style to map to the HTML border style - * - */ -void nsTableCellFrame::MapHTMLBorderStyle(nsIPresContext* aPresContext, - nsStyleBorder& aBorderStyle, - nsTableFrame* aTableFrame) -{ - //adjust the border style based on the table rules attribute - - /* The RULES code below has been disabled because collapsing borders have been disabled - and RULES depend on collapsing borders - - const nsStyleTable* tableStyle; - aTableFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle); - - switch (tableStyle->mRules) - { - case NS_STYLE_TABLE_RULES_NONE: - aBorderStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_NONE); - break; - - case NS_STYLE_TABLE_RULES_COLS: - aBorderStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - break; - - case NS_STYLE_TABLE_RULES_ROWS: - aBorderStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_NONE); - break; - - default: - // do nothing for "GROUPS" or "ALL" or for any illegal value - // "GROUPS" will be handled in nsTableFrame::ProcessGroupRules - break; - } - */ -} - - PRBool nsTableCellFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult) { if (aValue.GetUnit() == eHTMLUnit_Pixel) @@ -1401,13 +1355,16 @@ nsTableCellFrame::GetNextCellInColumn(nsITableCellLayout **aCellLayout) } nsresult -NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) +NS_NewTableCellFrame(nsIPresShell* aPresShell, + PRBool aIsBorderCollapse, + nsIFrame** aNewFrame) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } - nsTableCellFrame* it = new (aPresShell) nsTableCellFrame; + nsTableCellFrame* it = (aIsBorderCollapse) ? new (aPresShell) nsBCTableCellFrame + : new (aPresShell) nsTableCellFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } @@ -1415,27 +1372,16 @@ NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) return NS_OK; } - - -/* ----- methods from CellLayoutData ----- */ - -void -nsTableCellFrame::GetCellBorder(nsMargin& aBorder, - nsTableFrame* aTableFrame) +nsMargin* +nsTableCellFrame::GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const { aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0; - if (nsnull==aTableFrame) { - return; - } - if (NS_STYLE_BORDER_SEPARATE == aTableFrame->GetBorderCollapseStyle()) { - const nsStyleBorder* borderData; - GetStyleData(eStyleStruct_Border, (const nsStyleStruct*&)borderData); - borderData->GetBorder(aBorder); - } - else { - NS_ASSERTION(PR_FALSE, "not implemented"); - } + const nsStyleBorder* borderData; + GetStyleData(eStyleStruct_Border, (const nsStyleStruct*&)borderData); + borderData->GetBorder(aBorder); + return &aBorder; } NS_IMETHODIMP @@ -1502,3 +1448,100 @@ nsTableCellFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const return NS_OK; } #endif + +// nsBCTableCellFrame + +nsBCTableCellFrame::nsBCTableCellFrame() +:nsTableCellFrame() +{ + mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0; +} + +nsBCTableCellFrame::~nsBCTableCellFrame() +{ +} + +NS_IMETHODIMP +nsBCTableCellFrame::GetFrameType(nsIAtom** aType) const +{ + NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer"); + *aType = nsLayoutAtoms::bcTableCellFrame; + NS_ADDREF(*aType); + return NS_OK; +} + +#ifdef DEBUG +NS_IMETHODIMP +nsBCTableCellFrame::GetFrameName(nsAString& aResult) const +{ + return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult); +} +#endif + +void +nsBCTableCellFrame::SetBorderWidth(const nsMargin& aBorder) +{ + mTopBorder = aBorder.top; + mRightBorder = aBorder.right; + mBottomBorder = aBorder.bottom; + mLeftBorder = aBorder.left; +} + +nsMargin* +nsBCTableCellFrame::GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const +{ + aBorder.top = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mTopBorder) : mTopBorder; + aBorder.right = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mRightBorder) : mRightBorder; + aBorder.bottom = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mBottomBorder): mBottomBorder; + aBorder.left = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mLeftBorder): mLeftBorder; + return &aBorder; +} + +nscoord +nsBCTableCellFrame::GetBorderWidth(PRUint8 aSide) const +{ + switch(aSide) { + case NS_SIDE_TOP: + return (PRUint8)mTopBorder; + case NS_SIDE_RIGHT: + return (PRUint8)mRightBorder; + case NS_SIDE_BOTTOM: + return (PRUint8)mBottomBorder; + default: + return (PRUint8)mLeftBorder; + } +} + +void +nsBCTableCellFrame::SetBorderWidth(PRUint8 aSide, + nscoord aValue) +{ + switch(aSide) { + case NS_SIDE_TOP: + mTopBorder = aValue; + break; + case NS_SIDE_RIGHT: + mRightBorder = aValue; + break; + case NS_SIDE_BOTTOM: + mBottomBorder = aValue; + break; + default: + mLeftBorder = aValue; + } +} + +#ifdef DEBUG +NS_IMETHODIMP +nsBCTableCellFrame::SizeOf(nsISizeOfHandler* aHandler, + PRUint32* aResult) const +{ + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + PRUint32 sum = sizeof(*this); + *aResult = sum; + return NS_OK; +} +#endif diff --git a/mozilla/layout/html/table/src/nsTableCellFrame.h b/mozilla/layout/html/table/src/nsTableCellFrame.h index bba34cedb09..06474bfe35e 100644 --- a/mozilla/layout/html/table/src/nsTableCellFrame.h +++ b/mozilla/layout/html/table/src/nsTableCellFrame.h @@ -43,6 +43,7 @@ #include "nsTableRowFrame.h" // need to actually include this here to inline GetRowIndex #include "nsIStyleContext.h" #include "nsIPercentHeightObserver.h" +#include "nsLayoutAtoms.h" class nsTableFrame; class nsHTMLValue; @@ -266,6 +267,9 @@ public: nsTableCellFrame* GetNextCell() const; + virtual nsMargin* GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const; + protected: /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -277,18 +281,11 @@ private: // All these methods are support methods for RecalcLayoutData nsIFrame* GetFrameAt(nsVoidArray* aList, PRInt32 aIndex); - //XXX: aTableFrame can be removed as soon as border-collapse inherits correctly - void GetCellBorder(nsMargin &aBorder, nsTableFrame *aTableFrame); - protected: friend class nsTableRowFrame; void MapBorderPadding(nsIPresContext* aPresContext); - void MapHTMLBorderStyle(nsIPresContext* aPresContext, - nsStyleBorder& aBorderStyle, - nsTableFrame* aTableFrame); - void MapVAlignAttribute(nsIPresContext* aPresContext, nsTableFrame *aTableFrame); void MapHAlignAttribute(nsIPresContext* aPresContext, nsTableFrame *aTableFrame); @@ -421,6 +418,41 @@ inline void nsTableCellFrame::SetLastBlockHeight(nscoord aValue) { mBits.mLastBlockHeight = aValue; } + +// nsBCTableCellFrame +class nsBCTableCellFrame : public nsTableCellFrame +{ +public: + + nsBCTableCellFrame(); + + ~nsBCTableCellFrame(); + + NS_IMETHOD GetFrameType(nsIAtom** aType) const; + + virtual nsMargin* GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const; + nscoord GetBorderWidth(PRUint8 aSide) const; + + void SetBorderWidth(const nsMargin& aBorder); + void SetBorderWidth(PRUint8 aSide, nscoord aPixelValue); + +#ifdef DEBUG + NS_IMETHOD GetFrameName(nsAString& aResult) const; + NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const; +#endif + +private: + + PRUint32 mTopBorder: 8; + PRUint32 mRightBorder: 8; + PRUint32 mBottomBorder: 8; + PRUint32 mLeftBorder: 8; +}; + +#define IS_TABLE_CELL(frameType)\ +((nsLayoutAtoms::tableCellFrame == frameType) || (nsLayoutAtoms::bcTableCellFrame == frameType)) + #endif diff --git a/mozilla/layout/html/table/src/nsTableColFrame.cpp b/mozilla/layout/html/table/src/nsTableColFrame.cpp index ba970d16d9b..38368963da6 100644 --- a/mozilla/layout/html/table/src/nsTableColFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableColFrame.cpp @@ -113,7 +113,8 @@ nsStyleCoord nsTableColFrame::GetStyleWidth() const nsStyleCoord styleWidth = position->mWidth; // the following should not be necessary since html.css defines table-col and // :table-col to inherit. However, :table-col is not inheriting properly - if (eStyleUnit_Auto == styleWidth.GetUnit()) { + if (eStyleUnit_Auto == styleWidth.GetUnit() || + eStyleUnit_Inherit == styleWidth.GetUnit()) { nsIFrame* parent; GetParent(&parent); nsCOMPtr styleContext; @@ -305,6 +306,22 @@ nsTableColFrame::Init(nsIPresContext* aPresContext, return rv; } +nsTableColFrame* +nsTableColFrame::GetNextCol() const +{ + nsIFrame* childFrame; + GetNextSibling(&childFrame); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableColFrame == frameType.get()) { + return (nsTableColFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} + NS_IMETHODIMP nsTableColFrame::GetFrameType(nsIAtom** aType) const { diff --git a/mozilla/layout/html/table/src/nsTableColFrame.h b/mozilla/layout/html/table/src/nsTableColFrame.h index ba2d0042b04..505a84b3726 100644 --- a/mozilla/layout/html/table/src/nsTableColFrame.h +++ b/mozilla/layout/html/table/src/nsTableColFrame.h @@ -102,6 +102,8 @@ public: void SetColIndex (PRInt32 aColIndex); + nsTableColFrame* GetNextCol() const; + NS_IMETHOD Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, @@ -164,6 +166,11 @@ public: void ResetSizingInfo(); + nscoord GetLeftBorderWidth(float* aPixelsToTwips = nsnull); + void SetLeftBorderWidth(nscoord aWidth); + nscoord GetRightBorderWidth(float* aPixelsToTwips = nsnull); + void SetRightBorderWidth(nscoord aWidth); + void Dump(PRInt32 aIndent); protected: @@ -172,17 +179,46 @@ protected: ~nsTableColFrame(); // the starting index of the column (starting at 0) that this col object represents // - PRInt32 mColIndex; + PRUint32 mColIndex: 16; + PRUint32 mLeftBorderWidth: 8; // stored as pixels + PRUint32 mRightBorderWidth: 8; // stored as pixels + // Widths including MIN_CON, DES_CON, FIX_CON, MIN_ADJ, DES_ADJ, FIX_ADJ, PCT, PCT_ADJ, MIN_PRO, FINAL // Widths including MIN_CON, DES_CON, FIX_CON, MIN_ADJ, DES_ADJ, FIX_ADJ, PCT, PCT_ADJ, MIN_PRO, FINAL // XXX these could be stored as pixels and converted to twips for a savings of 10 x 2 bytes. nscoord mWidths[NUM_WIDTHS]; }; inline PRInt32 nsTableColFrame::GetColIndex() const -{ return mColIndex; } +{ + return mColIndex; +} inline void nsTableColFrame::SetColIndex (PRInt32 aColIndex) -{ mColIndex = aColIndex; } +{ + mColIndex = aColIndex; +} + +inline nscoord nsTableColFrame::GetLeftBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mLeftBorderWidth) : mLeftBorderWidth; + return width; +} + +inline void nsTableColFrame::SetLeftBorderWidth(nscoord aWidth) +{ + mLeftBorderWidth = aWidth; +} + +inline nscoord nsTableColFrame::GetRightBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mRightBorderWidth) : mRightBorderWidth; + return width; +} + +inline void nsTableColFrame::SetRightBorderWidth(nscoord aWidth) +{ + mRightBorderWidth = aWidth; +} #endif diff --git a/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp b/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp index 15c7dab5216..41a96c8d000 100644 --- a/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp @@ -250,29 +250,6 @@ nsTableColGroupFrame::SetInitialChildList(nsIPresContext* aPresContext, return NS_OK; } - nsIFrame* kidFrame = aChildList; - while (kidFrame) { - nsIAtom* kidType; - kidFrame->GetFrameType(&kidType); - if (nsLayoutAtoms::tableColFrame == kidType) { - // Set the preliminary values for the column frame - PRInt32 span = ((nsTableColFrame*)kidFrame)->GetSpan(); - if (span > 1) { - nsTableColFrame* firstSpannedCol; - tableFrame->CreateAnonymousColFrames(*aPresContext, *this, span - 1, eColAnonymousCol, - PR_FALSE, (nsTableColFrame*)kidFrame, (nsIFrame **)&firstSpannedCol); - nsIFrame* spanner = kidFrame; - kidFrame->GetNextSibling(&kidFrame); // need to do this before we insert the new frames - nsFrameList newChildren(aChildList); // used as a convience to hook up siblings - newChildren.InsertFrames(this, (nsTableColFrame*)spanner, (nsIFrame *)firstSpannedCol); - NS_RELEASE(kidType); - continue; - } - } - NS_IF_RELEASE(kidType); - kidFrame->GetNextSibling(&kidFrame); - } - mFrames.AppendFrames(this, aChildList); return NS_OK; } diff --git a/mozilla/layout/html/table/src/nsTableFrame.cpp b/mozilla/layout/html/table/src/nsTableFrame.cpp index 7a2d5dc94d6..718136af383 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableFrame.cpp @@ -38,7 +38,6 @@ #include "nsCOMPtr.h" #include "nsVoidArray.h" #include "nsTableFrame.h" -#include "nsTableBorderCollapser.h" #include "nsIRenderingContext.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" @@ -75,6 +74,7 @@ #include "nsIScrollableFrame.h" #include "nsHTMLReflowCommand.h" #include "nsIFrameManager.h" +#include "nsStyleUtil.h" // helper function for dealing with placeholder for positioned/floated table static void GetPlaceholderFor(nsIPresContext& aPresContext, nsIFrame& aFrame, nsIFrame** aPlaceholder); @@ -108,17 +108,19 @@ struct nsTableReflowState { // The first body section row group frame, i.e. not a header or footer nsIFrame* firstBodySection; - nsTableReflowState(const nsHTMLReflowState& aReflowState, + nsTableReflowState(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nsTableFrame& aTableFrame, nsReflowReason aReason, nscoord aAvailWidth, nscoord aAvailHeight) : reflowState(aReflowState) { - Init(aTableFrame, aReason, aAvailWidth, aAvailHeight); + Init(aPresContext, aTableFrame, aReason, aAvailWidth, aAvailHeight); } - void Init(nsTableFrame& aTableFrame, + void Init(nsIPresContext& aPresContext, + nsTableFrame& aTableFrame, nsReflowReason aReason, nscoord aAvailWidth, nscoord aAvailHeight) @@ -126,7 +128,7 @@ struct nsTableReflowState { reason = aReason; nsTableFrame* table = (nsTableFrame*)aTableFrame.GetFirstInFlow(); - nsMargin borderPadding = table->GetBorderPadding(reflowState); + nsMargin borderPadding = table->GetChildAreaOffset(aPresContext, &reflowState); x = borderPadding.left; y = borderPadding.top; @@ -145,23 +147,16 @@ struct nsTableReflowState { firstBodySection = nsnull; } - nsTableReflowState(const nsHTMLReflowState& aReflowState, + nsTableReflowState(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nsTableFrame& aTableFrame) : reflowState(aReflowState) { - Init(aTableFrame, aReflowState.reason, aReflowState.availableWidth, aReflowState.availableHeight); + Init(aPresContext, aTableFrame, aReflowState.reason, aReflowState.availableWidth, aReflowState.availableHeight); } }; -nscoord -GetHorBorderPaddingWidth(const nsHTMLReflowState& aReflowState, - nsTableFrame* aTableFrame) -{ - nsMargin borderPadding = aTableFrame->GetBorderPadding(aReflowState); - return borderPadding.left + borderPadding.right; -} - /******************************************************************************** ** nsTableFrame ** ********************************************************************************/ @@ -169,6 +164,18 @@ GetHorBorderPaddingWidth(const nsHTMLReflowState& aReflowState, static PRInt32 gRflCount = 0; #endif +struct BCPropertyData +{ + BCPropertyData() { mDamageArea.x = mDamageArea.y = mDamageArea.width = mDamageArea.height = + mTopBorderWidth = mRightBorderWidth = mBottomBorderWidth = mLeftBorderWidth = 0; } + nsRect mDamageArea; + PRUint8 mTopBorderWidth; + PRUint8 mRightBorderWidth; + PRUint8 mBottomBorderWidth; + PRUint8 mLeftBorderWidth; +}; + + NS_IMETHODIMP nsTableFrame::GetFrameType(nsIAtom** aType) const { @@ -193,6 +200,8 @@ nsTableFrame::nsTableFrame() mBits.mNeedStrategyBalance = PR_TRUE; mBits.mCellSpansPctCol = PR_FALSE; mBits.mRequestedTimeoutReflow = PR_FALSE; + mBits.mNeedToCalcBCBorders = PR_FALSE; + mBits.mIsBorderCollapse = PR_FALSE; #ifdef DEBUG_TABLE_REFLOW_TIMING mTimer = new nsReflowTimer(this); @@ -236,11 +245,6 @@ nsTableFrame::Init(nsIPresContext* aPresContext, { nsresult rv; - // Create the cell map - // XXX Why do we do this for continuing frames? - mCellMap = new nsTableCellMap(aPresContext, *this); - if (!mCellMap) return NS_ERROR_OUT_OF_MEMORY; - // Let the base class do its processing rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow); @@ -248,6 +252,16 @@ nsTableFrame::Init(nsIPresContext* aPresContext, // record that children that are ignorable whitespace should be excluded mState |= NS_FRAME_EXCLUDE_IGNORABLE_WHITESPACE; + // see if border collapse is on, if so set it + const nsStyleTableBorder* tableStyle = + (const nsStyleTableBorder*)mStyleContext->GetStyleData(eStyleStruct_TableBorder); + PRBool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); + SetBorderCollapse(borderCollapse); + // Create the cell map + // XXX Why do we do this for continuing frames? + mCellMap = new nsTableCellMap(aPresContext, *this, borderCollapse); + if (!mCellMap) return NS_ERROR_OUT_OF_MEMORY; + if (aPrevInFlow) { // set my width, because all frames in a table flow are the same width and // code in nsTableOuterFrame depends on this being set @@ -477,10 +491,11 @@ nsTableFrame::SetInitialChildList(nsIPresContext* aPresContext, // anonymous ones due to cells in rows. InsertColGroups(*aPresContext, 0, mColGroups.FirstChild()); AppendRowGroups(*aPresContext, mFrames.FirstChild()); - } - - if (HasGroupRules()) { - ProcessGroupRules(aPresContext); + // calc collapsing borders if this is the default (row group, col group, child list) + if (!aChildList && IsBorderCollapse()) { + nsRect damageArea(0, 0, GetColCount(), GetRowCount()); + SetBCDamageArea(*aPresContext, damageArea); + } } return rv; @@ -500,7 +515,7 @@ void nsTableFrame::AttributeChangedFor(nsIPresContext* aPresContext, { nsIAtom* frameType; aFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableCellFrame == frameType) { + if (IS_TABLE_CELL(frameType)) { if ((nsHTMLAtoms::rowspan == aAttribute) || (nsHTMLAtoms::colspan == aAttribute)) { nsTableCellMap* cellMap = GetCellMap(); @@ -550,7 +565,7 @@ PRInt32 nsTableFrame::GetRowCount () const } /* return the col count including dead cols */ -PRInt32 nsTableFrame::GetColCount () +PRInt32 nsTableFrame::GetColCount () const { PRInt32 colCount = 0; nsTableCellMap* cellMap = GetCellMap(); @@ -562,7 +577,7 @@ PRInt32 nsTableFrame::GetColCount () } /* return the effective col count */ -PRInt32 nsTableFrame::GetEffectiveColCount () +PRInt32 nsTableFrame::GetEffectiveColCount() const { PRInt32 colCount = GetColCount(); // don't count cols at the end that don't have originating cells @@ -659,66 +674,6 @@ PRInt32 nsTableFrame::GetEffectiveCOLSAttribute() return result; } -PRBool nsTableFrame::HasGroupRules() const -{ - const nsStyleTable* tableStyle = nsnull; - GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle); - if (NS_STYLE_TABLE_RULES_GROUPS == tableStyle->mRules) { - return PR_TRUE; - } - return PR_FALSE; -} - -// this won't work until bug 12948 is resolved and col groups are considered -void nsTableFrame::ProcessGroupRules(nsIPresContext* aPresContext) -{ -#if 0 - // The RULES code below has been disabled because collapsing borders have been disabled - // and RULES depend on collapsing borders - PRInt32 numCols = GetColCount(); - - // process row groups - nsIFrame* iFrame; - for (iFrame = mFrames.FirstChild(); iFrame; iFrame->GetNextSibling(&iFrame)) { - nsIAtom* frameType; - iFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableRowGroupFrame == frameType) { - nsTableRowGroupFrame* rgFrame = (nsTableRowGroupFrame *)iFrame; - PRInt32 startRow = rgFrame->GetStartRowIndex(); - PRInt32 numGroupRows = rgFrame->GetRowCount(); - PRInt32 endRow = startRow + numGroupRows - 1; - if (startRow == endRow) { - continue; - } - for (PRInt32 rowX = startRow; rowX <= endRow; rowX++) { - for (PRInt32 colX = 0; colX < numCols; colX++) { - PRBool originates; - nsTableCellFrame* cell = GetCellInfoAt(rowX, colX, &originates); - if (originates) { - nsCOMPtr styleContext; - cell->GetStyleContext(getter_AddRefs(styleContext)); - nsStyleBorder* border = (nsStyleBorder*)styleContext->GetMutableStyleData(eStyleStruct_Border); - if (rowX == startRow) { - border->SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - } - else if (rowX == endRow) { - border->SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - } - else { - border->SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - border->SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - } - styleContext->RecalcAutomaticData(aPresContext); - } - } - } - } - NS_IF_RELEASE(frameType); - } -#endif -} - - void nsTableFrame::AdjustRowIndices(nsIPresContext* aPresContext, PRInt32 aRowIndex, PRInt32 aAdjustment) @@ -833,6 +788,11 @@ void nsTableFrame::InsertCol(nsIPresContext& aPresContext, } } } + // for now, just bail and recalc all of the collapsing borders + if (IsBorderCollapse()) { + nsRect damageArea(0, 0, PR_MAX(1, GetColCount()), PR_MAX(1, GetRowCount())); + SetBCDamageArea(aPresContext, damageArea); + } } void nsTableFrame::RemoveCol(nsIPresContext& aPresContext, @@ -850,6 +810,11 @@ void nsTableFrame::RemoveCol(nsIPresContext& aPresContext, CreateAnonymousColFrames(aPresContext, 1, eColAnonymousCell, PR_TRUE); } } + // for now, just bail and recalc all of the collapsing borders + if (IsBorderCollapse()) { + nsRect damageArea(0, 0, GetColCount(), GetRowCount()); + SetBCDamageArea(aPresContext, damageArea); + } } /** Get the cell map for this table frame. It is not always mCellMap. @@ -1075,14 +1040,16 @@ nsTableFrame::CreateAnonymousColFrames(nsIPresContext& aPresContext, } } -PRInt32 nsTableFrame::AppendCell(nsIPresContext& aPresContext, - nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex) +void +nsTableFrame::AppendCell(nsIPresContext& aPresContext, + nsTableCellFrame& aCellFrame, + PRInt32 aRowIndex) { PRInt32 colIndex = 0; nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - colIndex = cellMap->AppendCell(aCellFrame, aRowIndex, PR_TRUE); + nsRect damageArea(0,0,0,0); + cellMap->AppendCell(aCellFrame, aRowIndex, PR_TRUE, damageArea); PRInt32 numColsInMap = GetColCount(); PRInt32 numColsInCache = mColFrames.Count(); PRInt32 numColsToAdd = numColsInMap - numColsInCache; @@ -1090,8 +1057,10 @@ PRInt32 nsTableFrame::AppendCell(nsIPresContext& aPresContext, // this sets the child list, updates the col cache and cell map CreateAnonymousColFrames(aPresContext, numColsToAdd, eColAnonymousCell, PR_TRUE); } + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } - return colIndex; } void nsTableFrame::InsertCells(nsIPresContext& aPresContext, @@ -1101,7 +1070,8 @@ void nsTableFrame::InsertCells(nsIPresContext& aPresContext, { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore); + nsRect damageArea(0,0,0,0); + cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea); PRInt32 numColsInMap = GetColCount(); PRInt32 numColsInCache = mColFrames.Count(); PRInt32 numColsToAdd = numColsInMap - numColsInCache; @@ -1109,6 +1079,9 @@ void nsTableFrame::InsertCells(nsIPresContext& aPresContext, // this sets the child list, updates the col cache and cell map CreateAnonymousColFrames(aPresContext, numColsToAdd, eColAnonymousCell, PR_TRUE); } + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } } @@ -1145,7 +1118,8 @@ void nsTableFrame::RemoveCell(nsIPresContext& aPresContext, { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - cellMap->RemoveCell(aCellFrame, aRowIndex); + nsRect damageArea(0,0,0,0); + cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea); PRInt32 numColsInMap = GetColCount(); // cell map's notion of num cols PRInt32 numColsInCache = mColFrames.Count(); if (numColsInCache > numColsInMap) { @@ -1156,6 +1130,10 @@ void nsTableFrame::RemoveCell(nsIPresContext& aPresContext, } } else NS_ASSERTION(numColsInCache == numColsInMap, "cell map has too many cols"); + + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } } @@ -1217,9 +1195,10 @@ nsTableFrame::InsertRows(nsIPresContext& aPresContext, PRInt32 numColsToAdd = 0; nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { + nsRect damageArea(0,0,0,0); PRInt32 origNumRows = cellMap->GetRowCount(); PRInt32 numNewRows = aRowFrames.Count(); - cellMap->InsertRows(&aPresContext, aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans); + cellMap->InsertRows(&aPresContext, aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea); PRInt32 numColsInMap = GetColCount(); // cell map's notion of num cols PRInt32 numColsInCache = mColFrames.Count(); numColsToAdd = numColsInMap - numColsInCache; @@ -1237,6 +1216,9 @@ nsTableFrame::InsertRows(nsIPresContext& aPresContext, nsTableRowFrame* rowFrame = (nsTableRowFrame *) aRowFrames.ElementAt(rowX); rowFrame->SetRowIndex(aRowIndex + rowX); } + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } //printf("insertRowsAfter \n"); @@ -1262,7 +1244,7 @@ void nsTableFrame::RemoveRows(nsIPresContext& aPresContext, for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking); kidFrame = kidFrame->GetNextSibling()) { nsCOMPtr kidType; kidFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == kidType.get()) { + if (IS_TABLE_CELL(kidType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)kidFrame; stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(), cellFrame->GetMaximumWidth(), PR_TRUE); @@ -1275,7 +1257,8 @@ void nsTableFrame::RemoveRows(nsIPresContext& aPresContext, PRInt32 firstRowIndex = aFirstRowFrame.GetRowIndex(); nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - cellMap->RemoveRows(&aPresContext, firstRowIndex, aNumRowsToRemove, aConsiderSpans); + nsRect damageArea(0,0,0,0); + cellMap->RemoveRows(&aPresContext, firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea); // only remove cols that are of type eTypeAnonymous cell (they are at the end) PRInt32 numColsInMap = GetColCount(); // cell map's notion of num cols PRInt32 numColsInCache = mColFrames.Count(); @@ -1287,6 +1270,10 @@ void nsTableFrame::RemoveRows(nsIPresContext& aPresContext, } } else NS_ASSERTION(numColsInCache == numColsInMap, "cell map has too many cols"); + + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } AdjustRowIndices(&aPresContext, firstRowIndex, -aNumRowsToRemove); //printf("removeRowsAfter\n"); @@ -1307,7 +1294,7 @@ void nsTableFrame::AppendRowGroups(nsIPresContext& aPresContext, nsTableRowGroupFrame* nsTableFrame::GetRowGroupFrame(nsIFrame* aFrame, - nsIAtom* aFrameTypeIn) + nsIAtom* aFrameTypeIn) const { nsIFrame* rgFrame = nsnull; nsIAtom* frameType = aFrameTypeIn; @@ -1479,12 +1466,12 @@ nsTableFrame::Paint(nsIPresContext* aPresContext, } PRIntn skipSides = GetSkipSides(); - if (NS_STYLE_BORDER_SEPARATE == GetBorderCollapseStyle()) { - nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, - aDirtyRect, rect, *border, mStyleContext, skipSides); + if (IsBorderCollapse()) { + PaintBCBorders(aPresContext, aRenderingContext, aDirtyRect); } else { - // tbd + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *border, mStyleContext, skipSides); } } } @@ -1703,6 +1690,7 @@ nsTableFrame::SetColumnDimensions(nsIPresContext* aPresContext, nscoord aHeight, const nsMargin& aBorderPadding) { + if (!aPresContext) ABORT0(); nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom; nscoord cellSpacingX = GetCellSpacingX(); @@ -1831,7 +1819,7 @@ nsTableFrame::NotifyAncestorsOfSpecialReflow(nsIFrame& aFrame) for (aFrame.GetParent(&parent); parent; parent->GetParent(&parent)) { nsCOMPtr frameType; parent->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { ((nsTableCellFrame*)parent)->SetNeedSpecialReflow(PR_TRUE); } else if (nsLayoutAtoms::tableRowFrame == frameType.get()) { @@ -1901,8 +1889,13 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); - - PRBool doCollapse = PR_FALSE; + + // see if collapsing borders need to be calculated + if (!mPrevInFlow && IsBorderCollapse() && NeedToCalcBCBorders()) { + GET_TWIPS_TO_PIXELS(aPresContext, p2t); + CalcBCBorders(*aPresContext); + } + PRBool doCollapse = PR_FALSE; // collapsing rows, cols, etc. aDesiredSize.width = aReflowState.availableWidth; @@ -1926,7 +1919,7 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, // only do pass1 reflow on an auto layout table nsReflowReason reason = (eReflowReason_Initial == aReflowState.reason) ? eReflowReason_Initial : eReflowReason_StyleChange; - nsTableReflowState reflowState(aReflowState, *this, reason, + nsTableReflowState reflowState(*aPresContext, aReflowState, *this, reason, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); // reflow the children ReflowChildren(aPresContext, reflowState, !HaveReflowedColGroups(), PR_FALSE, aStatus); @@ -1982,7 +1975,7 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, } // see if an extra reflow will be necessary in pagination mode when there is a specified table height else if (isPaginated && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aPresContext, aReflowState); if ((tableSpecifiedHeight > 0) && (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { SetNeedToInitiateSpecialReflow(PR_TRUE); @@ -2047,7 +2040,8 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, } #endif - SetColumnDimensions(aPresContext, aDesiredSize.height, aReflowState.mComputedBorderPadding); + nsMargin borderPadding = GetChildAreaOffset(*aPresContext, &aReflowState); + SetColumnDimensions(aPresContext, aDesiredSize.height, borderPadding); if (doCollapse) { AdjustForCollapsingRows(aPresContext, aDesiredSize.height); AdjustForCollapsingCols(aPresContext, aDesiredSize.width); @@ -2111,7 +2105,7 @@ nsTableFrame::ReflowTable(nsIPresContext* aPresContext, // Constrain our reflow width to the computed table width (of the 1st in flow). // and our reflow height to our avail height minus border, padding, cellspacing aDesiredSize.width = GetDesiredWidth(); - nsTableReflowState reflowState(aReflowState, *this, aReason, + nsTableReflowState reflowState(*aPresContext, aReflowState, *this, aReason, aDesiredSize.width, aAvailHeight); ReflowChildren(aPresContext, reflowState, haveReflowedColGroups, PR_FALSE, aStatus); @@ -2292,7 +2286,7 @@ nsTableFrame::CollapseRowGroupIfNecessary(nsIPresContext* aPresContext, PRInt32 numCols = cellMap->GetColCount(); nsTableCellFrame* lastCell = nsnull; for (int colX = 0; colX < numCols; colX++) { - CellData* cellData = cellMap->GetCellAt(aRowX, colX); + CellData* cellData = cellMap->GetDataAt(aRowX, colX); if (cellData && cellData->IsSpan()) { // a cell above is spanning into here // adjust the real cell's rect only once nsTableCellFrame* realCell = nsnull; @@ -2389,7 +2383,7 @@ NS_METHOD nsTableFrame::AdjustForCollapsingCols(nsIPresContext* aPresContext, nsTableCellFrame* lastCell = nsnull; nsTableCellFrame* cellFrame = nsnull; for (PRInt32 rowX = 0; rowX < numRows; rowX++) { - CellData* cellData = cellMap->GetCellAt(rowX, colX); + CellData* cellData = cellMap->GetDataAt(rowX, colX); nsRect cellRect; if (cellData) { if (cellData->IsOrig()) { // the cell originates at (rowX, colX) @@ -2670,7 +2664,7 @@ nsTableFrame::IncrementalReflow(nsIPresContext* aPresContext, nsTableFrame* table = (nsTableFrame*)GetFirstInFlow(); lastWidth = table->mRect.width; } - nsTableReflowState state(aReflowState, *this, eReflowReason_Incremental, + nsTableReflowState state(*aPresContext, aReflowState, *this, eReflowReason_Incremental, lastWidth, aReflowState.availableHeight); // determine if this frame is the target or not @@ -2721,7 +2715,7 @@ nsTableFrame::IR_TargetIsMe(nsIPresContext* aPresContext, break; case eReflowType_ReflowDirty: { // reflow the dirty children - nsTableReflowState reflowState(aReflowState.reflowState, *this, eReflowReason_Initial, + nsTableReflowState reflowState(*aPresContext, aReflowState.reflowState, *this, eReflowReason_Initial, aReflowState.availSize.width, aReflowState.availSize.height); PRBool reflowedAtLeastOne; ReflowChildren(aPresContext, reflowState, PR_FALSE, PR_TRUE, aStatus, &reflowedAtLeastOne); @@ -2753,25 +2747,127 @@ NS_METHOD nsTableFrame::IR_StyleChanged(nsIPresContext* aPresContext, return NS_OK; } -nsMargin -nsTableFrame::GetBorderPadding(const nsHTMLReflowState& aReflowState) const +static void +DivideBCBorderSize(nscoord aPixelSize, + nscoord& aSmallHalf, + nscoord& aLargeHalf) +{ + aSmallHalf = aPixelSize / 2; + aLargeHalf = ((aSmallHalf + aSmallHalf) < aPixelSize) ? aSmallHalf + 1 : aSmallHalf; +} + +nsMargin* +nsTableFrame::GetBCBorder(nsIPresContext& aPresContext, + PRBool aInnerBorderOnly, + nsMargin& aBorder) const +{ + aBorder.top = aBorder.right = aBorder.bottom = aBorder.left = 0; + + GET_PIXELS_TO_TWIPS(&aPresContext, p2t); + BCPropertyData* propData = + (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, (nsIFrame*)this, nsLayoutAtoms::tableBCProperty, PR_FALSE); + if (propData) { + nsCompatibility mode; + aPresContext.GetCompatibilityMode(&mode); + if ((eCompatibility_Standard == mode) || aInnerBorderOnly) { + nscoord smallHalf, largeHalf; + + DivideBCBorderSize(propData->mTopBorderWidth, smallHalf, largeHalf); + aBorder.top += NSToCoordRound(p2t * (float)smallHalf); + + DivideBCBorderSize(propData->mRightBorderWidth, smallHalf, largeHalf); + aBorder.right += NSToCoordRound(p2t * (float)largeHalf); + + DivideBCBorderSize(propData->mBottomBorderWidth, smallHalf, largeHalf); + aBorder.bottom += NSToCoordRound(p2t * (float)largeHalf); + + DivideBCBorderSize(propData->mLeftBorderWidth, smallHalf, largeHalf); + aBorder.left += NSToCoordRound(p2t * (float)smallHalf); + } + else { + aBorder.top += NSToCoordRound(p2t * (float)propData->mTopBorderWidth); + aBorder.right += NSToCoordRound(p2t * (float)propData->mRightBorderWidth); + aBorder.bottom += NSToCoordRound(p2t * (float)propData->mBottomBorderWidth); + aBorder.left += NSToCoordRound(p2t * (float)propData->mLeftBorderWidth); + } + } + return &aBorder; +} + +static +void GetSeparateModelBorderPadding(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState, + nsIStyleContext& aStyleContext, + nsMargin& aBorderPadding) { const nsStyleBorder* border = - (const nsStyleBorder*)mStyleContext->GetStyleData(eStyleStruct_Border); - nsMargin borderPadding; - border->GetBorder(borderPadding); - borderPadding += aReflowState.mComputedPadding; - return borderPadding; + (const nsStyleBorder*)aStyleContext.GetStyleData(eStyleStruct_Border); + border->GetBorder(aBorderPadding); + if (aReflowState) { + aBorderPadding += aReflowState->mComputedPadding; + } +} + +nsMargin +nsTableFrame::GetChildAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const +{ + nsMargin offset(0,0,0,0); + if (IsBorderCollapse()) { + nsCompatibility mode; + aPresContext.GetCompatibilityMode(&mode); + if (eCompatibility_NavQuirks == mode) { + nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT1(offset); + nscoord smallHalf, largeHalf; + GET_PIXELS_TO_TWIPS(&aPresContext, p2t); + BCPropertyData* propData = + (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, (nsIFrame*)firstInFlow, nsLayoutAtoms::tableBCProperty, PR_FALSE); + if (!propData) ABORT1(offset); + + DivideBCBorderSize(propData->mTopBorderWidth, smallHalf, largeHalf); + offset.top += NSToCoordRound(p2t * (float)largeHalf); + + DivideBCBorderSize(propData->mRightBorderWidth, smallHalf, largeHalf); + offset.right += NSToCoordRound(p2t * (float)smallHalf); + + DivideBCBorderSize(propData->mBottomBorderWidth, smallHalf, largeHalf); + offset.bottom += NSToCoordRound(p2t * (float)smallHalf); + + DivideBCBorderSize(propData->mLeftBorderWidth, smallHalf, largeHalf); + offset.left += NSToCoordRound(p2t * (float)largeHalf); + } + } + else { + if (!mStyleContext) ABORT1(offset); + GetSeparateModelBorderPadding(aPresContext, aReflowState, *mStyleContext, offset); + } + return offset; +} + +nsMargin +nsTableFrame::GetContentAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const +{ + nsMargin offset(0,0,0,0); + if (IsBorderCollapse()) { + GetBCBorder(aPresContext, PR_FALSE, offset); + } + else { + if (!mStyleContext) ABORT1(offset); + GetSeparateModelBorderPadding(aPresContext, aReflowState, *mStyleContext, offset); + } + return offset; } // Recovers the reflow state to what it should be if aKidFrame is about to be // reflowed. Restores y, footerFrame, firstBodySection and availSize.height (if // the height is constrained) nsresult -nsTableFrame::RecoverState(nsTableReflowState& aReflowState, +nsTableFrame::RecoverState(nsIPresContext& aPresContext, + nsTableReflowState& aReflowState, nsIFrame* aKidFrame) { - nsMargin borderPadding = GetBorderPadding(aReflowState.reflowState); + nsMargin borderPadding = GetChildAreaOffset(aPresContext, &aReflowState.reflowState); aReflowState.y = borderPadding.top; nscoord cellSpacingY = GetCellSpacingY(); @@ -2827,6 +2923,23 @@ nsTableFrame::RecoverState(nsTableReflowState& aReflowState, return NS_OK; } +void +nsTableFrame::InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState) +{ + nsMargin collapseBorder; + nsMargin padding(0,0,0,0); + nsMargin* pCollapseBorder = nsnull; + if (IsBorderCollapse()) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aReflowState.frame); + if (rgFrame) { + GET_PIXELS_TO_TWIPS(&aPresContext, p2t); + pCollapseBorder = rgFrame->GetBCBorderWidth(p2t, collapseBorder); + } + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); +} + NS_METHOD nsTableFrame::IR_TargetIsChild(nsIPresContext* aPresContext, nsTableReflowState& aReflowState, @@ -2834,9 +2947,10 @@ nsTableFrame::IR_TargetIsChild(nsIPresContext* aPresContext, nsIFrame* aNextFrame) { + if (!aPresContext) ABORT1(NS_ERROR_NULL_POINTER); nsresult rv; // Recover the state as if aNextFrame is about to be reflowed - RecoverState(aReflowState, aNextFrame); + RecoverState(*aPresContext, aReflowState, aNextFrame); // Remember the old rect nsRect oldKidRect; @@ -2844,8 +2958,10 @@ nsTableFrame::IR_TargetIsChild(nsIPresContext* aPresContext, // Pass along the reflow command, don't request a max element size, rows will do that nsHTMLReflowMetrics desiredSize(nsnull); - nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, - aNextFrame, aReflowState.availSize); + nsSize kidAvailSize(aReflowState.availSize); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, aNextFrame, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, kidReflowState); rv = ReflowChild(aNextFrame, aPresContext, desiredSize, kidReflowState, aReflowState.x, aReflowState.y, 0, aStatus); @@ -2969,7 +3085,7 @@ nsTableFrame::OrderRowGroups(nsVoidArray& aChildren, PRUint32& aNumRowGroups, nsIFrame** aFirstBody, nsTableRowGroupFrame** aHead, - nsTableRowGroupFrame** aFoot) + nsTableRowGroupFrame** aFoot) const { aChildren.Clear(); nsIFrame* head = nsnull; @@ -3069,6 +3185,10 @@ nsTableFrame::ReflowChildren(nsIPresContext* aPresContext, PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); + nsMargin collapseBorder; + nsMargin padding(0,0,0,0); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + nsAutoVoidArray rowGroups; PRUint32 numRowGroups; nsTableRowGroupFrame *thead, *tfoot; @@ -3117,8 +3237,9 @@ nsTableFrame::ReflowChildren(nsIPresContext* aPresContext, if (childX < numRowGroups) { // Reflow the child into the available space - nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, - kidFrame, kidAvailSize, aReflowState.reason); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, kidFrame, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, kidReflowState); // XXX fix up bad mComputedWidth for scroll frame kidReflowState.mComputedWidth = PR_MAX(kidReflowState.mComputedWidth, 0); @@ -3172,8 +3293,9 @@ nsTableFrame::ReflowChildren(nsIPresContext* aPresContext, } if (repeatedFooter) { kidAvailSize.height = repeatedFooterHeight; - nsHTMLReflowState footerReflowState(aPresContext, aReflowState.reflowState, - repeatedFooter, kidAvailSize, aReflowState.reason); + nsHTMLReflowState footerReflowState(aPresContext, aReflowState.reflowState, repeatedFooter, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, footerReflowState); aReflowState.y += cellSpacingY; nsReflowStatus footerStatus; rv = ReflowChild(repeatedFooter, aPresContext, desiredSize, footerReflowState, @@ -3268,7 +3390,7 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, nscoord minWidth, prefWidth; CalcMinAndPreferredWidths(aPresContext, aReflowState, PR_FALSE, minWidth, prefWidth); SetMinWidth(minWidth); - nscoord desWidth = CalcDesiredWidth(aReflowState); + nscoord desWidth = CalcDesiredWidth(*aPresContext, aReflowState); SetDesiredWidth(desWidth); SetPreferredWidth(prefWidth); @@ -3285,7 +3407,8 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, // This width is based on the column widths array of the table. // sum the width of each column and add in table insets nscoord -nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState) +nsTableFrame::CalcDesiredWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState) { NS_ASSERTION(!mPrevInFlow, "never ever call me on a continuing frame!"); nsTableCellMap* cellMap = GetCellMap(); @@ -3308,8 +3431,9 @@ nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState) if (numCols > 0) { tableWidth += cellSpacing; // add last cellspacing - // Compute the insets (sum of border and padding) - tableWidth += GetHorBorderPaddingWidth(aReflowState, this); + // Add the width between the border edge and the child area + nsMargin childOffset = GetChildAreaOffset(aPresContext, &aReflowState); + tableWidth += childOffset.left + childOffset.right; } return tableWidth; @@ -3326,7 +3450,7 @@ nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, return 0; } nscoord cellSpacingY = GetCellSpacingY(); - nsMargin borderPadding = GetBorderPadding(aReflowState); + nsMargin borderPadding = GetChildAreaOffset(*aPresContext, &aReflowState); // get the natural height based on the last child's (row group or scroll frame) rect nsAutoVoidArray rowGroups; @@ -3346,7 +3470,7 @@ nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, // see if a specified table height requires dividing additional space to rows if (!mPrevInFlow) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aPresContext, aReflowState); if ((tableSpecifiedHeight > 0) && (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && (tableSpecifiedHeight > desiredHeight)) { @@ -3391,7 +3515,9 @@ nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, nscoord cellSpacingY = GetCellSpacingY(); nscoord sumOfRowHeights = 0; - nscoord rowGroupYPos = aReflowState.mComputedBorderPadding.top + cellSpacingY; + nsMargin borderPadding; + GetChildAreaOffset(*aPresContext, &aReflowState); + nscoord rowGroupYPos = borderPadding.top + cellSpacingY; nsVoidArray rowGroups; PRUint32 numRowGroups; @@ -3402,7 +3528,7 @@ nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, // height, and base the pct on the table height. If the row group had a computed // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights nscoord pctBasis = aReflowState.mComputedHeight - (GetCellSpacingY() * (GetRowCount() + 1)); - nscoord yOriginRG = aReflowState.mComputedBorderPadding.top + GetCellSpacingY(); + nscoord yOriginRG = borderPadding.top + GetCellSpacingY(); nscoord yEndRG = yOriginRG; PRUint32 rgX; for (rgX = 0; (rgX < numRowGroups); rgX++) { @@ -3503,7 +3629,7 @@ nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, // allocate the extra height to the unstyled row groups and rows pctBasis = aAmount - amountUsed; - yOriginRG = aReflowState.mComputedBorderPadding.top + cellSpacingY; + yOriginRG = borderPadding.top + cellSpacingY; yEndRG = yOriginRG; for (rgX = 0; rgX < numRowGroups; rgX++) { nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); @@ -3817,61 +3943,6 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth) } } -// Update the border style to map to the HTML border style -void nsTableFrame::MapHTMLBorderStyle(nsStyleBorder& aBorderStyle, nscoord aBorderWidth) -{ - nsStyleCoord width; - width.SetCoordValue(aBorderWidth); - aBorderStyle.mBorder.SetTop(width); - aBorderStyle.mBorder.SetLeft(width); - aBorderStyle.mBorder.SetBottom(width); - aBorderStyle.mBorder.SetRight(width); - - aBorderStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_BG_OUTSET); - aBorderStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_BG_OUTSET); - aBorderStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_BG_OUTSET); - aBorderStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_BG_OUTSET); - - nsIStyleContext* styleContext = mStyleContext; - const nsStyleBackground* colorData = (const nsStyleBackground*) - styleContext->GetStyleData(eStyleStruct_Background); - - // Look until we find a style context with a NON-transparent background color - while (styleContext) { - if ((colorData->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) != 0) { - nsIStyleContext* temp = styleContext; - styleContext = styleContext->GetParent(); - if (temp != mStyleContext) - NS_RELEASE(temp); - colorData = (const nsStyleBackground*)styleContext->GetStyleData(eStyleStruct_Background); - } - else { - break; - } - } - - // Yaahoo, we found a style context which has a background color - - nscolor borderColor = 0xFFC0C0C0; - - if (styleContext) { - borderColor = colorData->mBackgroundColor; - if (styleContext != mStyleContext) - NS_RELEASE(styleContext); - } - - // if the border color is white, then shift to grey - if (borderColor == 0xFFFFFFFF) - borderColor = 0xFFC0C0C0; - - aBorderStyle.SetBorderColor(NS_SIDE_TOP, borderColor); - aBorderStyle.SetBorderColor(NS_SIDE_LEFT, borderColor); - aBorderStyle.SetBorderColor(NS_SIDE_BOTTOM, borderColor); - aBorderStyle.SetBorderColor(NS_SIDE_RIGHT, borderColor); - -} - - PRBool nsTableFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult) { @@ -3887,10 +3958,6 @@ PRBool nsTableFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, return PR_TRUE; } -void nsTableFrame::MapBorderMarginPadding(nsIPresContext* aPresContext) -{ -} - nscoord CalcPercentPadding(nscoord aBasis, nsStyleCoord aStyleCoord) @@ -3977,24 +4044,13 @@ nsTableFrame::GetPadding(const nsSize& aBasis, return padding; } -PRUint8 nsTableFrame::GetBorderCollapseStyle() -{ - /* the following has been commented out to turn off collapsing borders - const nsStyleTable* tableStyle; - GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle); - return tableStyle->mBorderCollapse;*/ - return NS_STYLE_BORDER_SEPARATE; -} - - // XXX: could cache this. But be sure to check style changes if you do! nscoord nsTableFrame::GetCellSpacingX() { - const nsStyleTableBorder* tableStyle; - GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); nscoord cellSpacing = 0; - PRUint8 borderCollapseStyle = GetBorderCollapseStyle(); - if (NS_STYLE_BORDER_COLLAPSE != borderCollapseStyle) { + if (!IsBorderCollapse()) { + const nsStyleTableBorder* tableStyle; + GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); if (tableStyle->mBorderSpacingX.GetUnit() == eStyleUnit_Coord) { cellSpacing = tableStyle->mBorderSpacingX.GetCoordValue(); } @@ -4005,11 +4061,10 @@ nscoord nsTableFrame::GetCellSpacingX() // XXX: could cache this. But be sure to check style changes if you do! nscoord nsTableFrame::GetCellSpacingY() { - const nsStyleTableBorder* tableStyle; - GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); nscoord cellSpacing = 0; - PRUint8 borderCollapseStyle = GetBorderCollapseStyle(); - if (NS_STYLE_BORDER_COLLAPSE != borderCollapseStyle) { + if (!IsBorderCollapse()) { + const nsStyleTableBorder* tableStyle; + GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); if (tableStyle->mBorderSpacingY.GetUnit() == eStyleUnit_Coord) { cellSpacing = tableStyle->mBorderSpacingY.GetCoordValue(); } @@ -4113,7 +4168,7 @@ nsTableFrame::CalcBorderBoxWidth(nsIPresContext* aPresContext, } } else if (width != NS_UNCONSTRAINEDSIZE) { - nsMargin borderPadding = aState.mComputedBorderPadding; + nsMargin borderPadding = GetContentAreaOffset(*aPresContext, &aState); width += borderPadding.left + borderPadding.right; } width = PR_MAX(width, 0); @@ -4128,11 +4183,12 @@ nsTableFrame::CalcBorderBoxWidth(nsIPresContext* aPresContext, } nscoord -nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState) +nsTableFrame::CalcBorderBoxHeight(nsIPresContext* aPresContext, + const nsHTMLReflowState& aState) { nscoord height = aState.mComputedHeight; if (NS_AUTOHEIGHT != height) { - nsMargin borderPadding = aState.mComputedBorderPadding; + nsMargin borderPadding = GetContentAreaOffset(*aPresContext, &aState); height += borderPadding.top + borderPadding.bottom; } height = PR_MAX(0, height); @@ -4180,6 +4236,7 @@ nsTableFrame::CalcMinAndPreferredWidths(nsIPresContext* aPresContext, nscoord& aMinWidth, nscoord& aPrefWidth) { + if (!aPresContext) ABORT0(); aMinWidth = aPrefWidth = 0; nscoord spacingX = GetCellSpacingX(); @@ -4201,7 +4258,8 @@ nsTableFrame::CalcMinAndPreferredWidths(nsIPresContext* aPresContext, } // if it is not a degenerate table, add the last spacing on the right and the borderPadding if (numCols > 0) { - nscoord extra = spacingX + GetHorBorderPaddingWidth(aReflowState, this); + nsMargin childAreaOffset = GetChildAreaOffset(*aPresContext, &aReflowState); + nscoord extra = spacingX + childAreaOffset.left + childAreaOffset.right; aMinWidth += extra; aPrefWidth += extra; } @@ -4217,14 +4275,15 @@ nsTableFrame::CalcMinAndPreferredWidths(nsIPresContext* aPresContext, if (mTableLayoutStrategy && IsAutoLayout()) { float p2t; aPresContext->GetPixelsToTwips(&p2t); - aPrefWidth = mTableLayoutStrategy->CalcPctAdjTableWidth(aReflowState, availWidth, p2t); + aPrefWidth = mTableLayoutStrategy->CalcPctAdjTableWidth(*aPresContext, aReflowState, availWidth, p2t); } } } else { // a specified fix width becomes the min or preferred width nscoord compWidth = aReflowState.mComputedWidth; if ((NS_UNCONSTRAINEDSIZE != compWidth) && (0 != compWidth) && !isPctWidth) { - compWidth += GetHorBorderPaddingWidth(aReflowState, this); + nsMargin contentOffset = GetContentAreaOffset(*aPresContext, &aReflowState); + compWidth += contentOffset.left + contentOffset.right; aMinWidth = PR_MAX(aMinWidth, compWidth); aPrefWidth = PR_MAX(aMinWidth, compWidth); } @@ -4288,7 +4347,7 @@ nsTableFrame::DumpRowGroup(nsIPresContext* aPresContext, nsIFrame* aKidFrame) while (cellFrame) { nsIAtom* cellType; cellFrame->GetFrameType(&cellType); - if (nsLayoutAtoms::tableCellFrame == cellType) { + if (IS_TABLE_CELL(cellType)) { PRInt32 colIndex; ((nsTableCellFrame*)cellFrame)->GetColIndex(colIndex); printf("cell(%d)=%p ", colIndex, cellFrame); @@ -4557,6 +4616,2361 @@ PRInt32 nsTableFrame::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const return 0; } +/******************************************************************************** + * Collapsing Borders + * + * The CSS spec says to resolve border conflicts in this order: + * 1) any border with the style HIDDEN wins + * 2) the widest border with a style that is not NONE wins + * 3) the border styles are ranked in this order, highest to lowest precedence: + * double, solid, dashed, dotted, ridge, outset, groove, inset + * 4) borders that are of equal width and style (differ only in color) have this precedence: + * cell, row, rowgroup, col, colgroup, table + * 5) if all border styles are NONE, then that's the computed border style. + *******************************************************************************/ + +void +nsTableFrame::SetBCDamageArea(nsIPresContext& aPresContext, + const nsRect& aValue) +{ + nsRect newRect(aValue); + newRect.width = PR_MAX(1, newRect.width); + newRect.height = PR_MAX(1, newRect.height); + + if (!IsBorderCollapse()) { + NS_ASSERTION(PR_FALSE, "invalid call - not border collapse model"); + return; + } + SetNeedToCalcBCBorders(PR_TRUE); + // Get the property + BCPropertyData* value = (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, this, nsLayoutAtoms::tableBCProperty, PR_TRUE); + if (value) { + // for now just construct a union of the new and old damage areas + value->mDamageArea.UnionRect(value->mDamageArea, newRect); + } +} +/***************************************************************** + * BCMapCellIterator + ****************************************************************/ +struct BCMapCellInfo +{ + BCMapCellInfo(); + void Reset(); + + CellData* cellData; + nsCellMap* cellMap; + + nsTableRowGroupFrame* rg; + + nsTableRowFrame* topRow; + nsTableRowFrame* bottomRow; + + nsTableColGroupFrame* cg; + + nsTableColFrame* leftCol; + nsTableColFrame* rightCol; + + nsBCTableCellFrame* cell; + + PRInt32 rowIndex; + PRInt32 rowSpan; + PRInt32 colIndex; + PRInt32 colSpan; + + PRPackedBool rgTop; + PRPackedBool rgBottom; + PRPackedBool cgLeft; + PRPackedBool cgRight; +}; + +BCMapCellInfo::BCMapCellInfo() +{ + Reset(); +} + +void BCMapCellInfo::Reset() +{ + cellData = nsnull; + rg = nsnull; + topRow = nsnull; + bottomRow = nsnull; + cg = nsnull; + leftCol = nsnull; + rightCol = nsnull; + cell = nsnull; + rowIndex = rowSpan = colIndex = colSpan = 0; + rgTop = rgBottom = cgLeft = cgRight = PR_FALSE; +} + +class BCMapCellIterator +{ +public: + BCMapCellIterator(nsTableFrame& aTableFrame, + const nsRect& aDamageArea); + + void First(BCMapCellInfo& aMapCellInfo); + + void Next(BCMapCellInfo& aMapCellInfo); + + void PeekRight(BCMapCellInfo& aRefInfo, + PRUint32 aRowIndex, + BCMapCellInfo& aAjaInfo); + + void PeekBottom(BCMapCellInfo& aRefInfo, + PRUint32 aColIndex, + BCMapCellInfo& aAjaInfo); + + PRBool IsNewRow() { return mIsNewRow; } + + nsTableRowFrame* GetPrevRow() const { return mPrevRow; } + + PRInt32 mRowGroupStart; + PRInt32 mRowGroupEnd; + PRBool mAtEnd; + nsCellMap* mCellMap; + +private: + void SetInfo(nsTableRowFrame* aRow, + PRInt32 aColIndex, + CellData* aCellData, + BCMapCellInfo& aMapInfo, + nsCellMap* aCellMap = nsnull); + + PRBool SetNewRow(nsTableRowFrame* row = nsnull); + PRBool SetNewRowGroup(); + + nsTableFrame& mTableFrame; + nsTableCellMap* mTableCellMap; + nsVoidArray mRowGroups; + nsTableRowGroupFrame* mRowGroup; + PRInt32 mRowGroupIndex; + PRUint32 mNumRows; + nsTableRowFrame* mRow; + nsTableRowFrame* mPrevRow; + PRBool mIsNewRow; + PRInt32 mRowIndex; + PRUint32 mNumCols; + PRInt32 mColIndex; + nsPoint mAreaStart; + nsPoint mAreaEnd; +}; + +BCMapCellIterator::BCMapCellIterator(nsTableFrame& aTableFrame, + const nsRect& aDamageArea) +:mTableFrame(aTableFrame) +{ + mTableCellMap = aTableFrame.GetCellMap(); + + mAreaStart.x = aDamageArea.x; + mAreaStart.y = aDamageArea.y; + mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1; + mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1; + + mNumRows = mTableFrame.GetRowCount(); + mRow = nsnull; + mRowIndex = 0; + mNumCols = mTableFrame.GetColCount(); + mColIndex = 0; + mRowGroupIndex = -1; + + // Get the ordered row groups + PRUint32 numRowGroups; + aTableFrame.OrderRowGroups(mRowGroups, numRowGroups, nsnull); + + mAtEnd = PR_TRUE; // gets reset when First() is called +} + +void +BCMapCellIterator::SetInfo(nsTableRowFrame* aRow, + PRInt32 aColIndex, + CellData* aCellData, + BCMapCellInfo& aCellInfo, + nsCellMap* aCellMap) +{ + aCellInfo.cellData = aCellData; + aCellInfo.cellMap = (aCellMap) ? aCellMap : mCellMap; + aCellInfo.colIndex = aColIndex; + + // row frame info + aCellInfo.rowIndex = 0; + if (aRow) { + aCellInfo.topRow = aRow; + aCellInfo.rowIndex = aRow->GetRowIndex(); + } + + // cell frame info + aCellInfo.cell = nsnull; + aCellInfo.rowSpan = 1; + aCellInfo.colSpan = 1; + if (aCellData) { + aCellInfo.cell = (nsBCTableCellFrame*)aCellData->GetCellFrame(); + if (aCellInfo.cell) { + if (!aCellInfo.topRow) { + aCellInfo.cell->GetParent((nsIFrame**)&aCellInfo.topRow); if (!aCellInfo.topRow) ABORT0(); + aCellInfo.rowIndex = aCellInfo.topRow->GetRowIndex(); + } + PRBool ignore; + PRInt32 rgRowIndex = aCellInfo.rowIndex - mRowGroupStart; + aCellInfo.colSpan = mCellMap->GetEffectiveColSpan(*mTableCellMap, rgRowIndex, aColIndex, ignore); + aCellInfo.rowSpan = mCellMap->GetRowSpan(*mTableCellMap, rgRowIndex, aColIndex, PR_TRUE, ignore); + } + } + if (!aCellInfo.topRow) { + aCellInfo.topRow = mRow; + } + + if (1 == aCellInfo.rowSpan) { + aCellInfo.bottomRow = aCellInfo.topRow; + } + else { + aCellInfo.bottomRow = aCellInfo.topRow->GetNextRow(); + if (aCellInfo.bottomRow) { + for (PRInt32 spanX = 2; aCellInfo.bottomRow && (spanX < aCellInfo.rowSpan); spanX++) { + aCellInfo.bottomRow = aCellInfo.bottomRow->GetNextRow(); + } + NS_ASSERTION(aCellInfo.bottomRow, "program error"); + } + else { + NS_ASSERTION(PR_FALSE, "error in cell map"); + aCellInfo.rowSpan = 1; + aCellInfo.bottomRow = aCellInfo.topRow; + } + } + + // row group frame info + PRUint32 rgStart = mRowGroupStart; + PRUint32 rgEnd = mRowGroupEnd; + nsIFrame* parentFrame = nsnull; + aCellInfo.topRow->GetParent((nsIFrame**)&parentFrame); + aCellInfo.rg = mTableFrame.GetRowGroupFrame(parentFrame); + if (aCellInfo.rg != mRowGroup) { + rgStart = aCellInfo.rg->GetStartRowIndex(); + rgEnd = rgStart + aCellInfo.rg->GetRowCount() - 1; + } + PRUint32 rowIndex = aCellInfo.topRow->GetRowIndex(); + aCellInfo.rgTop = (rgStart == rowIndex); + aCellInfo.rgBottom = (rgEnd == rowIndex + aCellInfo.rowSpan - 1); + + // col frame info + aCellInfo.leftCol = mTableFrame.GetColFrame(aColIndex); if (!aCellInfo.leftCol) ABORT0(); + + aCellInfo.rightCol = aCellInfo.leftCol; + if (aCellInfo.colSpan > 1) { + for (PRInt32 spanX = 1; spanX < aCellInfo.colSpan; spanX++) { + nsTableColFrame* colFrame = aCellInfo.rightCol->GetNextCol(); + if (colFrame) { + aCellInfo.rightCol = colFrame; + } + else { + NS_ASSERTION(PR_FALSE, "program error"); + break; + } + } + } + + // col group frame info + aCellInfo.leftCol->GetParent((nsIFrame**)&aCellInfo.cg); + PRInt32 cgStart = aCellInfo.cg->GetStartColumnIndex(); + PRInt32 cgEnd = PR_MAX(0, cgStart + aCellInfo.cg->GetColCount() - 1); + aCellInfo.cgLeft = (cgStart == aColIndex); + aCellInfo.cgRight = (cgEnd == aColIndex + (PRInt32)aCellInfo.colSpan - 1); +} + +PRBool +BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow) +{ + mPrevRow = mRow; + if (aRow) { + mRow = aRow; + } + else if (mRow) { + mRow = mRow->GetNextRow(); + } + if (mRow) { + mRowIndex = mRow->GetRowIndex(); + // get to the first entry with an originating cell + PRInt32 rgRowIndex = mRowIndex - mRowGroupStart; + nsVoidArray* row = (nsVoidArray*)mCellMap->mRows.ElementAt(rgRowIndex); if (!row) ABORT1(PR_FALSE); + PRInt32 rowSize = row->Count(); + for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) { + CellData* cellData = (mColIndex < rowSize) ? (CellData*)row->ElementAt(mColIndex) : nsnull; + if (!cellData) { // add a dead cell data + nsRect damageArea; + cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT1(PR_FALSE); + } + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { + break; + } + } + mIsNewRow = PR_TRUE; + } + else { + NS_ASSERTION(PR_FALSE, "program error"); + mAtEnd = PR_TRUE; + } + return !mAtEnd; +} + +PRBool +BCMapCellIterator::SetNewRowGroup() +{ + mRowGroupIndex++; + PRInt32 numRowGroups = mRowGroups.Count(); + for (PRInt32 rgX = mRowGroupIndex; (rgX < numRowGroups) && !mAtEnd; rgX++) { + nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(mRowGroupIndex); if (!frame) ABORT1(PR_FALSE); + mRowGroup = mTableFrame.GetRowGroupFrame(frame); if (!mRowGroup) ABORT1(PR_FALSE); + mRowGroupStart = mRowGroup->GetStartRowIndex(); + mRowGroupEnd = mRowGroupStart + mRowGroup->GetRowCount() - 1; + if (mRowGroupEnd >= 0) { + mCellMap = mTableCellMap->GetMapFor(*mRowGroup); + if (SetNewRow(mRowGroup->GetFirstRow())) { + return PR_TRUE; + } + else mAtEnd = PR_TRUE; + } + } + mAtEnd = PR_TRUE; + return !mAtEnd; +} + +void +BCMapCellIterator::First(BCMapCellInfo& aMapInfo) +{ + mAtEnd = PR_FALSE; + aMapInfo.Reset(); + + SetNewRowGroup(); // could set mAtEnd + while (!mAtEnd) { + if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { + CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, mAreaStart.y - mRowGroupStart, mAreaStart.x, PR_FALSE); + if (cellData) { + if (!cellData->IsOrig()) { + // if the start data does not have an originating cell, adjust it to have one + if (cellData->IsRowSpan()) { + mAreaStart.y -= cellData->GetRowSpanOffset(); + NS_ASSERTION(mAreaStart.y >= 0, "program error"); + } + if (cellData->IsColSpan()) { + mAreaStart.x -= cellData->GetColSpanOffset(); + NS_ASSERTION(mAreaStart.x >= 0, "program error"); + } + cellData = mCellMap->GetDataAt(*mTableCellMap, mAreaStart.y - mRowGroupStart, mAreaStart.x, PR_FALSE); + } + if (cellData && cellData->IsOrig()) { + SetInfo(mRow, mAreaStart.x, cellData, aMapInfo); + break; + } + else mAtEnd = PR_TRUE; + } + else mAtEnd = PR_TRUE; + } + SetNewRowGroup(); // could set mAtEnd + } +} + +void +BCMapCellIterator::Next(BCMapCellInfo& aMapInfo) +{ + if (mAtEnd) ABORT0(); + aMapInfo.Reset(); + + mIsNewRow = PR_FALSE; + mColIndex++; + while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) { + for (; mColIndex <= mAreaEnd.x; mColIndex++) { + PRInt32 rgRowIndex = mRowIndex - mRowGroupStart; + CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, mColIndex, PR_FALSE); + if (!cellData) { // add a dead cell data + nsRect damageArea; + cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { + SetInfo(mRow, mColIndex, cellData, aMapInfo); + return; + } + } + if (mRowIndex >= mRowGroupEnd) { + SetNewRowGroup(); // could set mAtEnd + } + else { + SetNewRow(); // could set mAtEnd + } + } + mAtEnd = PR_TRUE; +} + +void +BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo, + PRUint32 aRowIndex, + BCMapCellInfo& aAjaInfo) +{ + aAjaInfo.Reset(); + PRInt32 colIndex = aRefInfo.colIndex + aRefInfo.colSpan; + PRUint32 rgRowIndex = aRowIndex - mRowGroupStart; + + CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, colIndex, PR_FALSE); + if (!cellData) { // add a dead cell data + NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error"); + nsRect damageArea; + cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + nsTableRowFrame* row = nsnull; + if (cellData->IsRowSpan()) { + rgRowIndex -= cellData->GetRowSpanOffset(); + cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, colIndex, PR_FALSE); if (!cellData) ABORT0(); + } + else { + row = mRow; + } + SetInfo(row, colIndex, cellData, aAjaInfo); +} + +void +BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo, + PRUint32 aColIndex, + BCMapCellInfo& aAjaInfo) +{ + aAjaInfo.Reset(); + PRInt32 rowIndex = aRefInfo.rowIndex + aRefInfo.rowSpan; + PRInt32 rgRowIndex = rowIndex - mRowGroupStart; + nsTableRowGroupFrame* rg = mRowGroup; + nsCellMap* cellMap = mCellMap; + nsTableRowFrame* nextRow = nsnull; + if (rowIndex > mRowGroupEnd) { + nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(mRowGroupIndex + 1); if (!frame) ABORT0(); + rg = mTableFrame.GetRowGroupFrame(frame); + if (rg) { + cellMap = mTableCellMap->GetMapFor(*rg); + rgRowIndex = 0; + nextRow = rg->GetFirstRow(); + } + else return; + } + else { + // get the row within the same row group + nextRow = mRow; + for (PRInt32 i = 0; i < aRefInfo.rowSpan; i++) { + nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0(); + } + } + + CellData* cellData = cellMap->GetDataAt(*mTableCellMap, rgRowIndex, aColIndex, PR_FALSE); + if (!cellData) { // add a dead cell data + NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error"); + nsRect damageArea; + cellData = cellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + if (cellData->IsColSpan()) { + aColIndex -= cellData->GetColSpanOffset(); + cellData = cellMap->GetDataAt(*mTableCellMap, rowIndex, aColIndex, PR_FALSE); + } + SetInfo(nextRow, aColIndex, cellData, aAjaInfo, cellMap); +} + +// Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID) +// will return the priority of NS_STYLE_BORDER_STYLE_SOLID. +static PRUint8 styleToPriority[13] = { 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 7, 1, 2 }; + +// Set aStyle and aWidth given aStyleBorder and aSide +static void +GetStyleInfo(const nsIFrame& aFrame, + PRUint8 aSide, + PRUint8& aStyle, + nscolor& aColor, + PRBool aIgnoreIfRules = PR_FALSE, + nscoord* aWidth = nsnull, + float* aTwipsToPixels = nsnull) +{ + const nsStyleBorder* styleData; + aFrame.GetStyleData(eStyleStruct_Border, (const nsStyleStruct *&)styleData); if (!styleData) ABORT0(); + + aStyle = styleData->GetBorderStyle(aSide); + + // if the rules mask is set, set the style either to none or remove the mask + if (NS_STYLE_BORDER_STYLE_RULES_MASK & aStyle) { + if (aIgnoreIfRules) { + aStyle = NS_STYLE_BORDER_STYLE_NONE; + aColor = 0; + if (aWidth) { + *aWidth = 0; + } + return; + } + else { + aStyle &= ~NS_STYLE_BORDER_STYLE_RULES_MASK; + } + } + + if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { + if (aWidth) { + *aWidth = 0; + aColor = 0; + } + return; + } + + if ((NS_STYLE_BORDER_STYLE_INSET == aStyle) || + (NS_STYLE_BORDER_STYLE_BG_INSET == aStyle)) { + aStyle = NS_STYLE_BORDER_STYLE_GROOVE; + } + else if ((NS_STYLE_BORDER_STYLE_OUTSET == aStyle) || + (NS_STYLE_BORDER_STYLE_BG_OUTSET == aStyle)) { + aStyle = NS_STYLE_BORDER_STYLE_RIDGE; + } + PRBool transparent, foreground; + styleData->GetBorderColor(aSide, aColor, transparent, foreground); + if (foreground) { + nsCOMPtr styleContext; + aFrame.GetStyleContext(getter_AddRefs(styleContext)); if(!styleContext) ABORT0(); + const nsStyleColor* colorStyle = (const nsStyleColor*)styleContext->GetStyleData(eStyleStruct_Color); + aColor = colorStyle->mColor; + } + if (aWidth && aTwipsToPixels) { + *aWidth = 0; + nscoord width; + styleData->CalcBorderFor(&aFrame, aSide, width); + *aWidth = NSToCoordRound(*aTwipsToPixels * (float)width); + } +} + +static PRBool +CalcDominateBorder(PRBool aIsCorner, + BCBorderOwner aOwner1, + PRUint8 aStyle1, + PRUint16 aWidth1, + nscolor aColor1, + BCBorderOwner aOwner2, + PRUint8 aStyle2, + PRUint16 aWidth2, + nscolor aColor2, + BCBorderOwner& aDomOwner, + PRUint8& aDomStyle, + PRUint16& aDomWidth, + nscolor& aDomColor, + PRBool aSecondIsHorizontal) +{ + PRBool firstDominates = PR_TRUE; + if (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle1) { + firstDominates = (aIsCorner) ? PR_FALSE : PR_TRUE; + } + else if (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle2) { + firstDominates = (aIsCorner) ? PR_TRUE : PR_FALSE; + } + else if (aWidth1 < aWidth2) { + firstDominates = PR_FALSE; + } + else if (aWidth1 == aWidth2) { + if (styleToPriority[aStyle1] < styleToPriority[aStyle2]) { + firstDominates = PR_FALSE; + } + else if (styleToPriority[aStyle1] == styleToPriority[aStyle2]) { + if (aOwner1 == aOwner2) { + firstDominates = !aSecondIsHorizontal; + } + else if (aOwner1 < aOwner2) { + firstDominates = PR_FALSE; + } + } + } + if (firstDominates) { + aDomOwner = aOwner1; + aDomStyle = aStyle1; + aDomWidth = aWidth1; + aDomColor = aColor1; + } + else { + aDomOwner = aOwner2; + aDomStyle = aStyle2; + aDomWidth = aWidth2; + aDomColor = aColor2; + } + return firstDominates; +} + +// calc the dominate border by considering the table, row/col group, row/col, cell, +static void +CalcDominateBorder(const nsIFrame* aTableFrame, + const nsIFrame* aColGroupFrame, + const nsIFrame* aColFrame, + const nsIFrame* aRowGroupFrame, + const nsIFrame* aRowFrame, + const nsIFrame* aCellFrame, + PRBool aIgnoreIfRules, + PRUint8 aSide, + PRBool aAja, + float aTwipsToPixels, + BCBorderOwner& aDomElem, + PRUint8& aDomStyle, + PRUint16& aDomWidth, + nscolor& aDomColor) +{ + PRUint8 style; + nscolor color; + aDomStyle = NS_STYLE_BORDER_STYLE_NONE; + nscoord width; + aDomWidth = 0; + PRBool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide); + + // start with the table as dominate if present + if (aTableFrame) { + GetStyleInfo(*aTableFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = eTableOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + // see if the col row group is dominate + if (aColGroupFrame) { + GetStyleInfo(*aColGroupFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the col is dominate + if (aColFrame) { + GetStyleInfo(*aColFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && !horizontal) ? eAjaColOwner : eColOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the row row group is dominate + if (aRowGroupFrame) { + GetStyleInfo(*aRowGroupFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the row is dominate + if (aRowFrame) { + GetStyleInfo(*aRowFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && horizontal) ? eAjaRowOwner : eRowOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the cell is dominate + if (aCellFrame) { + GetStyleInfo(*aCellFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja) ? eAjaCellOwner : eCellOwner; + } + } +} + +static PRBool +Perpendicular(PRUint8 aSide1, + PRUint8 aSide2) +{ + switch (aSide1) { + case NS_SIDE_TOP: + return (NS_SIDE_BOTTOM != aSide2); + case NS_SIDE_RIGHT: + return (NS_SIDE_LEFT != aSide2); + case NS_SIDE_BOTTOM: + return (NS_SIDE_TOP != aSide2); + default: // NS_SIDE_LEFT + return (NS_SIDE_RIGHT != aSide2); + } +} + +// XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1 +struct BCCornerInfo +{ + BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerSide = ownerElem = subSide = + subElem = hasDashDot = numSegs = bevel = 0; + ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; } + void Set(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor); + + void Update(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor); + + nscolor ownerColor; // color of borderOwner + PRUint16 ownerWidth; // pixel width of borderOwner + PRUint16 subWidth; // pixel width of the largest border intersecting the border perpendicular + // to ownerSide + PRUint32 ownerSide:2; // side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border owning + // the corner relative to the corner + PRUint32 ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner + PRUint32 ownerStyle:8; // border style of ownerElem + PRUint32 subSide:2; // side of border with subWidth relative to the corner + PRUint32 subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner + PRUint32 subStyle:8; // border style of subElem + PRUint32 hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled + PRUint32 numSegs:3; // number of segments entering corner + PRUint32 bevel:1; // is the corner beveled (uses the above two fields together with subWidth) + PRUint32 unused:1; +}; + +void +BCCornerInfo::Set(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor) +{ + ownerElem = aBorderOwner; + ownerStyle = aOwnerBStyle; + ownerWidth = aOwnerWidth; + ownerColor = aOwnerColor; + ownerSide = aSide; + hasDashDot = 0; + numSegs = 0; + if (aOwnerWidth > 0) { + numSegs++; + hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aOwnerBStyle) || + (NS_STYLE_BORDER_STYLE_DOTTED == aOwnerBStyle); + } + bevel = 0; + subWidth = 0; + // the following will get set later + subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT; + subElem = eTableOwner; + subStyle = NS_STYLE_BORDER_STYLE_SOLID; +} + +void +BCCornerInfo::Update(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor) +{ + PRBool existingWins = PR_FALSE; + if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet + Set(aSide, aBorderOwner, aOwnerBStyle, aOwnerWidth, aOwnerColor); + } + else { + PRBool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner + PRUint8 oldElem = ownerElem; + PRUint8 oldSide = ownerSide; + PRUint8 oldStyle = ownerStyle; + nscoord oldWidth = ownerWidth; + BCBorderOwner tempBorderOwner = (BCBorderOwner)ownerElem; + PRUint8 tempStyle = ownerStyle; + existingWins = + CalcDominateBorder(PR_TRUE, (BCBorderOwner)ownerElem, ownerStyle, ownerWidth, ownerColor, + (BCBorderOwner)aBorderOwner, aOwnerBStyle, aOwnerWidth, aOwnerColor, + tempBorderOwner, tempStyle, ownerWidth, ownerColor, horizontal); + ownerElem = tempBorderOwner; + ownerStyle = tempStyle; + if (existingWins) { // existing corner is dominate + if (::Perpendicular(ownerSide, aSide)) { + // see if the new sub info replaces the old + nscolor color; + tempBorderOwner = (BCBorderOwner)ownerElem; + PRUint8 tempStyle = subStyle; + PRBool firstWins = + CalcDominateBorder(PR_TRUE, (BCBorderOwner)subElem, subStyle, subWidth, color, + (BCBorderOwner)aBorderOwner, aOwnerBStyle, aOwnerWidth, aOwnerColor, + tempBorderOwner, tempStyle, subWidth, color, horizontal); + subElem = tempBorderOwner; + subStyle = tempStyle; + if (firstWins) { + subSide = aSide; + } + } + } + else { // input args are dominate + ownerSide = aSide; + if (::Perpendicular(oldSide, ownerSide)) { + subElem = oldElem; + subSide = oldSide; + subStyle = oldStyle; + subWidth = oldWidth; + } + } + if (aOwnerWidth > 0) { + numSegs++; + if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aOwnerBStyle) || + (NS_STYLE_BORDER_STYLE_DOTTED == aOwnerBStyle))) { + hasDashDot = 1; + } + } + + // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner + bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot); + } +} + +struct BCCorners +{ + BCCorners(PRInt32 aNumCorners, + PRInt32 aStartIndex); + + ~BCCorners() { delete [] corners; } + + BCCornerInfo& operator [](PRInt32 i) const + { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); + return corners[i - startIndex]; } + + PRInt32 startIndex; + PRInt32 endIndex; + BCCornerInfo* corners; +}; + +BCCorners::BCCorners(PRInt32 aNumCorners, + PRInt32 aStartIndex) +{ + NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error"); + startIndex = aStartIndex; + endIndex = aStartIndex + aNumCorners - 1; + corners = new BCCornerInfo[aNumCorners]; +} + +struct BCCellBorder +{ + BCCellBorder() { Reset(); } + void Reset(PRUint32 aIndex = 0, PRUint32 aSpan = 1); + nscolor color; + PRUint16 index; // y index, not used for vertical borders + PRUint16 span; // row span + PRUint16 width; + PRUint8 style; +}; + +void +BCCellBorder::Reset(PRUint32 aIndex, + PRUint32 aSpan) +{ + style = color = width = -1; + index = (PRUint16)aIndex; + span = (PRUint16)aSpan; +} + +struct BCCellBorders +{ + BCCellBorders(PRInt32 aNumBorders, + PRInt32 aStartIndex); + + ~BCCellBorders() { delete [] borders; } + + BCCellBorder& operator [](PRInt32 i) const + { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); + return borders[i - startIndex]; } + + PRInt32 startIndex; + PRInt32 endIndex; + BCCellBorder* borders; +}; + +BCCellBorders::BCCellBorders(PRInt32 aNumBorders, + PRInt32 aStartIndex) +{ + NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error"); + startIndex = aStartIndex; + endIndex = aStartIndex + aNumBorders - 1; + borders = new BCCellBorder[aNumBorders]; +} + +static PRBool +SetBorder(PRUint8 aOwnerBStyle, + PRUint16 aOwnerWidth, + nscolor aOwnerColor, + BCCellBorder& aBorder) +{ + PRBool changed = (aOwnerBStyle != aBorder.style) || (aOwnerWidth != aBorder.width) || + (aOwnerColor != aBorder.color); + aBorder.color = aOwnerColor; + aBorder.width = aOwnerWidth; + aBorder.style = aOwnerBStyle; + + return changed; +} + +static PRBool +SetHorBorder(PRUint8 aOwnerBStyle, + PRUint16 aOwnerWidth, + PRUint32 aOwnerColor, + const BCCornerInfo& aCorner, + BCCellBorder& aBorder) +{ + PRBool startSeg = ::SetBorder(aOwnerBStyle, aOwnerWidth, aOwnerColor, aBorder); + if (!startSeg) { + startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide)); + } + return startSeg; +} + +// Make the damage area larger on the top and bottom by at least one row and on the left and right +// at least one column. This is done so that adjacent elements are part of the border calculations. +// The extra segments and borders outside the actual damage area will not be updated in the cell map, +// because they in turn would need info from adjacent segments outside the damage area to be accurate. +void +nsTableFrame::ExpandBCDamageArea(nsRect& aRect) const +{ + PRInt32 numRows = GetRowCount(); + PRInt32 numCols = GetColCount(); + + PRInt32 dStartX = aRect.x; + PRInt32 dEndX = aRect.XMost() - 1; + PRInt32 dStartY = aRect.y; + PRInt32 dEndY = aRect.YMost() - 1; + + // expand the damage area in each direction + if (dStartX > 0) { + dStartX--; + } + if (dEndX < (numCols - 1)) { + dEndX++; + } + if (dStartY > 0) { + dStartY--; + } + if (dEndY < (numRows - 1)) { + dEndY++; + } + // Check the damage area so that there are no cells spanning in or out. If there are any then + // make the damage area as big as the table, similarly to the way the cell map decides whether + // to rebuild versus expand. This could be optimized to expand to the smallest area that contains + // no spanners, but it may not be worth the effort in general, and it would need to be done in the + // cell map as well. + if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) { + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); + PRBool haveSpanner = PR_FALSE; + // Get the ordered row groups + PRUint32 numRowGroups; + nsVoidArray rowGroups; + OrderRowGroups(rowGroups, numRowGroups, nsnull); + for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { + nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX); + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0(); + PRInt32 rgStartY = rgFrame->GetStartRowIndex(); + PRInt32 rgEndY = rgStartY + rgFrame->GetRowCount() - 1; + if (dEndY < rgStartY) + break; + nsCellMap* cellMap = tableCellMap->GetMapFor(*rgFrame); if (!cellMap) ABORT0(); + // check for spanners from above and below + if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) { + nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(dStartY - rgStartY); if (!row) ABORT0(); + for (PRInt32 x = dStartX; x <= dEndX; x++) { + CellData* cellData = (row->Count() > x) ? (CellData*)row->ElementAt(x) : nsnull; + if (cellData && (cellData->IsRowSpan())) { + haveSpanner = PR_TRUE; + break; + } + } + if (dEndY < rgEndY) { + row = (nsVoidArray*)cellMap->mRows.ElementAt(dEndY + 1 - rgStartY); if (!row) ABORT0(); + for (PRInt32 x = dStartX; x <= dEndX; x++) { + CellData* cellData = (row->Count() > x) ? (CellData*)row->ElementAt(x) : nsnull; + if (cellData && (cellData->IsRowSpan())) { + haveSpanner = PR_TRUE; + break; + } + } + } + } + // check for spanners on the left and right + PRInt32 iterStartY = -1; + PRInt32 iterEndY = -1; + if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) { + // the damage area starts in the row group + iterStartY = dStartY; + iterEndY = PR_MIN(dEndY, rgEndY); + } + else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) { + // the damage area ends in the row group + iterStartY = rgStartY; + iterEndY = PR_MIN(dEndY, rgStartY); + } + else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) { + // the damage area contains the row group + iterStartY = rgStartY; + iterEndY = rgEndY; + } + if ((iterStartY >= 0) && (iterEndY >= 0)) { + for (PRInt32 y = iterStartY; y <= iterEndY; y++) { + nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(y - rgStartY); if (!row) ABORT0(); + CellData* cellData = (CellData*)row->ElementAt(dStartX); + if (cellData && (cellData->IsColSpan())) { + haveSpanner = PR_TRUE; + break; + } + if (dEndX < (numCols - 1)) { + cellData = (row->Count() > dEndX) ? (CellData*)row->ElementAt(dEndX + 1) : nsnull; + if (cellData && (cellData->IsColSpan())) { + haveSpanner = PR_TRUE; + break; + } + } + } + } + } + } + aRect.x = dStartX; + aRect.y = dStartY; + aRect.width = 1 + dEndX - dStartX; + aRect.height = 1 + dEndY - dStartY; +} + +#define MAX_TABLE_BORDER_WIDTH 256 +static PRUint8 +LimitBorderWidth(PRUint16 aWidth) +{ + return PR_MIN(MAX_TABLE_BORDER_WIDTH, aWidth); +} + +/* Here is the order for storing border edges in the cell map as a cell is processed. There are + n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell. + + 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since + a bottom edge from a cell above will take care of it. + 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since + a right edge from a cell to the left will take care of it. + 3) Store the right edge (or edges if a row span) + 4) Store the bottom edge (or edges if a col span) + + Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner + calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries + would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing + borders already have about an extra 8 byte per cell map entry overhead (this could be + reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the + rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the + left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border + edges per cell and n=rowspan left and right border edges per cell. + + 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table. + Never store the top-right corner, since it will get stored as a right-top corner. + 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner, + since it will get stored as a bottom-left corner. + 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on + the top edge of the table. Never store the right-bottom corner since it will get stored as a + bottom-right corner. + 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it + otherwise, since it will get stored as either a right-top corner by a cell below or + a bottom-left corner from a cell to the right. + 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits + the top side of a colspan in its interior. Never store the corner otherwise, since it will + get stored as a right-top corner by a cell from below. + */ + +#define TOP_DAMAGED(aRowIndex) ((aRowIndex) >= propData->mDamageArea.y) +#define RIGHT_DAMAGED(aColIndex) ((aColIndex) <= propData->mDamageArea.XMost()) +#define BOTTOM_DAMAGED(aRowIndex) ((aRowIndex) <= propData->mDamageArea.YMost()) +#define LEFT_DAMAGED(aColIndex) ((aColIndex) >= propData->mDamageArea.x) + +// Calc the dominate border at every cell edge and corner within the current damage area +void +nsTableFrame::CalcBCBorders(nsIPresContext& aPresContext) +{ + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); + PRInt32 numRows = GetRowCount(); + PRInt32 numCols = GetColCount(); + + // Get the property holding the table damage area and border widths + BCPropertyData* propData = + (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, this, nsLayoutAtoms::tableBCProperty, PR_FALSE); + if (!propData) ABORT0(); + // calculate an expanded damage area + nsRect damageArea(propData->mDamageArea); + ExpandBCDamageArea(damageArea); + + // segments that are on the table border edges need to be initialized only once + PRBool tableBorderReset[4]; + for (PRUint32 sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) { + tableBorderReset[sideX] = PR_FALSE; + } + GET_TWIPS_TO_PIXELS(&aPresContext, t2p); + + const nsStyleBorder* tableStyle = + (const nsStyleBorder*)mStyleContext->GetStyleData(eStyleStruct_Border); + + // vertical borders indexed in x-direction (cols) + BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); if (!lastVerBorders.borders) ABORT0(); + BCCellBorder lastTopBorder, lastBottomBorder; + // horizontal borders indexed in x-direction (cols) + BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); if (!lastBottomBorders.borders) ABORT0(); + PRBool startSeg; + + BCMapCellInfo info, ajaInfo; + BCBorderOwner owner, ajaOwner; + nscolor ownerColor, ajaColor; + PRUint8 ownerBStyle, ajaBStyle; + PRUint16 ownerWidth, ajaWidth; + PRInt32 cellEndRowIndex = -1; + PRInt32 cellEndColIndex = -1; + nscoord smallHalf, largeHalf; + BCCorners topCorners(damageArea.width + 1, damageArea.x); if (!topCorners.corners) ABORT0(); + BCCorners bottomCorners(damageArea.width + 1, damageArea.x); if (!bottomCorners.corners) ABORT0(); + nsTableRowFrame* prevRow = nsnull; + + BCMapCellIterator iter(*this, damageArea); + for (iter.First(info); !iter.mAtEnd; iter.Next(info)) { + + cellEndRowIndex = info.rowIndex + info.rowSpan - 1; + cellEndColIndex = info.colIndex + info.colSpan - 1; + PRBool isBottomRight = (info.rowIndex == (numRows - 1)) && (info.colIndex == (numCols - 1)); + + PRBool bottomRowSpan = PR_FALSE; + // see if lastTopBorder, lastBottomBorder need to be reset + if (iter.IsNewRow()) { + lastTopBorder.Reset(info.rowIndex, info.rowSpan); + lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan); + } + else if (info.colIndex > damageArea.x) { + BCCellBorder& prevBorder = lastBottomBorders[info.colIndex - 1]; + if (info.rowIndex > prevBorder.index - prevBorder.span) { + // the top border's left edge butts against the middle of a rowspan + lastTopBorder.Reset(info.rowIndex, info.rowSpan); + } + if (prevBorder.index > (cellEndRowIndex + 1)) { + // the bottom border's left edge butts against the middle of a rowspan + lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan); + bottomRowSpan = PR_TRUE; + } + } + + // find the dominate border considernig the cell's top border and the table, row group, row + // if the border is at the top of the table, otherwise it was processed in a previous row + if (0 == info.rowIndex) { + if (!tableBorderReset[NS_SIDE_TOP]) { + propData->mTopBorderWidth = 0; + tableBorderReset[NS_SIDE_TOP] = PR_TRUE; + } + for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) { + nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0(); + nsIFrame* cgFrame; + colFrame->GetParent(&cgFrame); if (!cgFrame) ABORT0(); + CalcDominateBorder(this, cgFrame, colFrame, info.rg, info.topRow, info.cell, PR_TRUE, NS_SIDE_TOP, + PR_FALSE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + // update/store the top left & top right corners of the seg + BCCornerInfo& tlCorner = topCorners[colX]; // top left + if (0 == colX) { + tlCorner.Set(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + } + else { + tlCorner.Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, 0, 0, colX, + tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel); + } + topCorners[colX + 1].Set(NS_SIDE_LEFT, owner, ownerBStyle, ownerWidth, ownerColor); // top right + // update lastTopBorder and see if a new segment starts + startSeg = SetHorBorder(ownerBStyle, ownerWidth, ownerColor, tlCorner, lastTopBorder); + // store the border segment in the cell map + tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *info.cellMap, 0, 0, colX, + 1, owner, ownerWidth, startSeg); + // update the affected borders of the cell, row, and table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(smallHalf, info.cell->GetBorderWidth(NS_SIDE_TOP))); + } + if (info.topRow) { + info.topRow->SetTopBCBorderWidth(PR_MAX(smallHalf, info.topRow->GetTopBCBorderWidth())); + } + propData->mTopBorderWidth = LimitBorderWidth(PR_MAX(propData->mTopBorderWidth, (PRUint8)ownerWidth)); + } + } + else { + // see if the top border needs to be the start of a segment due to a vertical border owning the corner + if (info.colIndex > 0) { + BCData& data = ((BCCellData*)info.cellData)->mData; + if (!data.IsTopStart()) { + PRUint8 cornerSide; + PRPackedBool bevel; + data.GetCorner(cornerSide, bevel); + if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) { + data.SetTopStart(PR_TRUE); + } + } + } + } + + // find the dominate border considernig the cell's left border and the table, col group, col + // if the border is at the left of the table, otherwise it was processed in a previous col + if (0 == info.colIndex) { + if (!tableBorderReset[NS_SIDE_LEFT]) { + propData->mLeftBorderWidth = 0; + tableBorderReset[NS_SIDE_LEFT] = PR_TRUE; + } + for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) { + nsTableRowFrame* rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow(); + CalcDominateBorder(this, info.cg, info.leftCol, info.rg, rowFrame, info.cell, PR_TRUE, NS_SIDE_LEFT, + PR_FALSE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + BCCornerInfo& tlCorner = (0 == rowX) ? topCorners[0] : bottomCorners[0]; // top left + tlCorner.Update(NS_SIDE_BOTTOM, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, iter.mRowGroupStart, rowX, + 0, tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel); + bottomCorners[0].Set(NS_SIDE_TOP, owner, ownerBStyle, ownerWidth, ownerColor); // bottom left + // update lastVerBordersBorder and see if a new segment starts + startSeg = SetBorder(ownerBStyle, ownerWidth, ownerColor, lastVerBorders[0]); + // store the border segment in the cell map + tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *info.cellMap, iter.mRowGroupStart, rowX, + info.colIndex, 1, owner, ownerWidth, startSeg); + // update the left border of the cell, col and table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_LEFT, PR_MAX(smallHalf, info.cell->GetBorderWidth(NS_SIDE_LEFT))); + } + if (info.leftCol) { + info.leftCol->SetLeftBorderWidth(PR_MAX(smallHalf, info.leftCol->GetLeftBorderWidth())); + } + propData->mLeftBorderWidth = LimitBorderWidth(PR_MAX(propData->mLeftBorderWidth, ownerWidth)); + } + } + + // find the dominate border considernig the cell's right border, adjacent cells and the table, row group, row + if (numCols == cellEndColIndex + 1) { // touches right edge of table + if (!tableBorderReset[NS_SIDE_RIGHT]) { + propData->mRightBorderWidth = 0; + tableBorderReset[NS_SIDE_RIGHT] = PR_TRUE; + } + for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) { + nsTableRowFrame* rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow(); + CalcDominateBorder(this, info.cg, info.rightCol, info.rg, rowFrame, info.cell, PR_TRUE, NS_SIDE_RIGHT, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + // update/store the top right & bottom right corners + BCCornerInfo& trCorner = (0 == rowX) ? topCorners[cellEndColIndex + 1] : bottomCorners[cellEndColIndex + 1]; + trCorner.Update(NS_SIDE_BOTTOM, owner, ownerBStyle, ownerWidth, ownerColor); // top right + tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, trCorner.ownerSide, trCorner.subWidth, trCorner.bevel); + BCCornerInfo& brCorner = bottomCorners[cellEndColIndex + 1]; + brCorner.Set(NS_SIDE_TOP, owner, ownerBStyle, ownerWidth, ownerColor); // bottom right + tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel); + // update lastVerBorders and see if a new segment starts + startSeg = SetBorder(ownerBStyle, ownerWidth, ownerColor, lastVerBorders[cellEndColIndex + 1]); + // store the border segment in the cell map and update cellBorders + tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, 1, owner, ownerWidth, startSeg); + // update the affected borders of the cell, col, and table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_RIGHT, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_RIGHT))); + } + if (info.rightCol) { + info.rightCol->SetRightBorderWidth(PR_MAX(largeHalf, info.rightCol->GetRightBorderWidth())); + } + propData->mRightBorderWidth = LimitBorderWidth(PR_MAX(propData->mRightBorderWidth, ownerWidth)); + } + } + else { + PRInt32 segLength = 0; + BCMapCellInfo priorAjaInfo; + for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX += segLength) { + iter.PeekRight(info, rowX, ajaInfo); + const nsIFrame* cg = (info.cgRight) ? info.cg : nsnull; + CalcDominateBorder(nsnull, cg, info.rightCol, nsnull, nsnull, info.cell, PR_FALSE, NS_SIDE_RIGHT, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + cg = (ajaInfo.cgLeft) ? ajaInfo.cg : nsnull; + CalcDominateBorder(nsnull, cg, ajaInfo.leftCol, nsnull, nsnull, ajaInfo.cell, PR_FALSE, NS_SIDE_LEFT, + PR_FALSE, t2p, ajaOwner, ajaBStyle, ajaWidth, ajaColor); + CalcDominateBorder(PR_FALSE, owner, ownerBStyle, ownerWidth, ownerColor, ajaOwner, ajaBStyle, + ajaWidth, ajaColor, owner, ownerBStyle, ownerWidth, ownerColor, PR_FALSE); + segLength = PR_MAX(1, ajaInfo.rowIndex + ajaInfo.rowSpan - rowX); + segLength = PR_MIN(segLength, info.rowIndex + info.rowSpan - rowX); + + // update lastVerBorders and see if a new segment starts + startSeg = SetBorder(ownerBStyle, ownerWidth, ownerColor, lastVerBorders[cellEndColIndex + 1]); + // store the border segment in the cell map and update cellBorders + if (RIGHT_DAMAGED(cellEndColIndex) && TOP_DAMAGED(rowX) && BOTTOM_DAMAGED(rowX)) { + tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, segLength, owner, ownerWidth, startSeg); + // update the borders of the cells and cols affected + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_RIGHT, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_RIGHT))); + } + if (info.rightCol) { + info.rightCol->SetRightBorderWidth(PR_MAX(largeHalf, info.rightCol->GetRightBorderWidth())); + } + if (ajaInfo.cell) { + ajaInfo.cell->SetBorderWidth(NS_SIDE_LEFT, PR_MAX(smallHalf, ajaInfo.cell->GetBorderWidth(NS_SIDE_LEFT))); + } + if (ajaInfo.leftCol) { + ajaInfo.leftCol->SetLeftBorderWidth(PR_MAX(smallHalf, ajaInfo.leftCol->GetLeftBorderWidth())); + } + } + // update the top right corner + PRBool hitsSpanOnRight = (rowX > ajaInfo.rowIndex) && (rowX < ajaInfo.rowIndex + ajaInfo.rowSpan); + BCCornerInfo* trCorner = ((0 == rowX) || hitsSpanOnRight) + ? &topCorners[cellEndColIndex + 1] : &bottomCorners[cellEndColIndex + 1]; + trCorner->Update(NS_SIDE_BOTTOM, owner, ownerBStyle, ownerWidth, ownerColor); + // if this is not the first time through, consider the segment to the right + if (rowX != info.rowIndex) { + const nsIFrame* rg = (priorAjaInfo.rgBottom) ? priorAjaInfo.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, priorAjaInfo.bottomRow, priorAjaInfo.cell, PR_FALSE, + NS_SIDE_BOTTOM, PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell, PR_FALSE, NS_SIDE_TOP, + PR_FALSE, t2p, ajaOwner, ajaBStyle, ajaWidth, ajaColor); + CalcDominateBorder(PR_FALSE, owner, ownerBStyle, ownerWidth, ownerColor, ajaOwner, ajaBStyle, + ajaWidth, ajaColor, owner, ownerBStyle, ownerWidth, ownerColor, PR_TRUE); + trCorner->Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + } + // store the top right corner in the cell map + if (RIGHT_DAMAGED(cellEndColIndex) && TOP_DAMAGED(rowX)) { + if (0 != rowX) { + tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, cellEndColIndex, + trCorner->ownerSide, trCorner->subWidth, trCorner->bevel); + } + // store any corners this cell spans together with the aja cell + for (PRInt32 rX = rowX + 1; rX < rowX + segLength; rX++) { + tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rX, + cellEndColIndex, trCorner->ownerSide, trCorner->subWidth, PR_FALSE); + } + } + // update bottom right corner, topCorners, bottomCorners + hitsSpanOnRight = (rowX + segLength < ajaInfo.rowIndex + ajaInfo.rowSpan); + BCCornerInfo& brCorner = (hitsSpanOnRight) ? topCorners[cellEndColIndex + 1] + : bottomCorners[cellEndColIndex + 1]; + brCorner.Set(NS_SIDE_TOP, owner, ownerBStyle, ownerWidth, ownerColor); + priorAjaInfo = ajaInfo; + } + } + for (PRInt32 colX = info.colIndex + 1; colX <= cellEndColIndex; colX++) { + lastVerBorders[colX].Reset(); + } + + // find the dominate border considernig the cell's bottom border, adjacent cells and the table, row group, row + if (numRows == cellEndRowIndex + 1) { // touches botom edge of table + if (!tableBorderReset[NS_SIDE_BOTTOM]) { + propData->mBottomBorderWidth = 0; + tableBorderReset[NS_SIDE_BOTTOM] = PR_TRUE; + } + for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) { + nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0(); + nsIFrame* cgFrame; + colFrame->GetParent(&cgFrame); if (!cgFrame) ABORT0(); + CalcDominateBorder(this, cgFrame, colFrame, info.rg, info.bottomRow, info.cell, PR_TRUE, NS_SIDE_BOTTOM, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + // update/store the bottom left & bottom right corners + BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left + blCorner.Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, blCorner.ownerSide, blCorner.subWidth, blCorner.bevel); + BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right + brCorner.Update(NS_SIDE_LEFT, owner, ownerBStyle, ownerWidth, ownerColor); + if (numCols == colX + 1) { // lower right corner of the table + tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel, isBottomRight); + } + // update lastBottomBorder and see if a new segment starts + startSeg = SetHorBorder(ownerBStyle, ownerWidth, ownerColor, blCorner, lastBottomBorder); + // store the border segment in the cell map and update cellBorders + tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, 1, owner, ownerWidth, startSeg); + // update the bottom borders of the cell, the bottom row, and the table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_BOTTOM))); + } + if (info.bottomRow) { + info.bottomRow->SetBottomBCBorderWidth(PR_MAX(largeHalf, info.bottomRow->GetBottomBCBorderWidth())); + } + propData->mBottomBorderWidth = LimitBorderWidth(PR_MAX(propData->mBottomBorderWidth, ownerWidth)); + // update lastBottomBorders + lastBottomBorder.index = cellEndRowIndex + 1; + lastBottomBorder.span = info.rowSpan; + lastBottomBorders[colX] = lastBottomBorder; + } + } + else { + PRInt32 segLength = 0; + for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX += segLength) { + iter.PeekBottom(info, colX, ajaInfo); + const nsIFrame* rg = (info.rgBottom) ? info.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, info.bottomRow, info.cell, PR_FALSE, NS_SIDE_BOTTOM, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell, PR_FALSE, NS_SIDE_TOP, + PR_FALSE, t2p, ajaOwner, ajaBStyle, ajaWidth, ajaColor); + CalcDominateBorder(PR_FALSE, owner, ownerBStyle, ownerWidth, ownerColor, ajaOwner, ajaBStyle, ajaWidth, + ajaColor, owner, ownerBStyle, ownerWidth, ownerColor, PR_TRUE); + segLength = PR_MAX(1, ajaInfo.colIndex + ajaInfo.colSpan - colX); + segLength = PR_MIN(segLength, info.colIndex + info.colSpan - colX); + + // update, store the bottom left corner + BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left + PRBool hitsSpanBelow = (colX > ajaInfo.colIndex) && (colX < ajaInfo.colIndex + ajaInfo.colSpan); + PRBool update = PR_TRUE; + if ((colX == info.colIndex) && (colX > damageArea.x)) { + PRInt32 prevRowIndex = lastBottomBorders[colX - 1].index; + if (prevRowIndex > cellEndRowIndex + 1) { // hits a rowspan on the right + update = PR_FALSE; // the corner was taken care of during the cell on the left + } + else if (prevRowIndex < cellEndRowIndex) { // spans below the cell to the left + topCorners[colX] = blCorner; + blCorner.Set(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + update = PR_FALSE; + } + } + if (update) { + blCorner.Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + } + if (BOTTOM_DAMAGED(cellEndRowIndex) && LEFT_DAMAGED(colX)) { + if (hitsSpanBelow) { + tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, colX, + blCorner.ownerSide, blCorner.subWidth, blCorner.bevel); + } + // store any corners this cell spans together with the aja cell + for (PRInt32 cX = colX + 1; cX < colX + segLength; cX++) { + BCCornerInfo& corner = bottomCorners[cX]; + corner.Set(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + cX, corner.ownerSide, corner.subWidth, PR_FALSE); + } + } + // update lastBottomBorders and see if a new segment starts + startSeg = SetHorBorder(ownerBStyle, ownerWidth, ownerColor, blCorner, lastBottomBorder); + lastBottomBorder.index = cellEndRowIndex + 1; + lastBottomBorder.span = info.rowSpan; + for (PRInt32 cX = colX; cX < colX + segLength; cX++) { + lastBottomBorders[cX] = lastBottomBorder; + } + + // store the border segment the cell map and update cellBorders + if (BOTTOM_DAMAGED(cellEndRowIndex) && LEFT_DAMAGED(colX) && RIGHT_DAMAGED(colX)) { + tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, segLength, owner, ownerWidth, startSeg); + // update the borders of the affected cells and rows + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_BOTTOM))); + } + if (info.bottomRow) { + info.bottomRow->SetBottomBCBorderWidth(PR_MAX(largeHalf, info.bottomRow->GetBottomBCBorderWidth())); + } + if (ajaInfo.cell) { + ajaInfo.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(smallHalf, ajaInfo.cell->GetBorderWidth(NS_SIDE_TOP))); + } + if (ajaInfo.topRow) { + ajaInfo.topRow->SetTopBCBorderWidth(PR_MAX(smallHalf, ajaInfo.topRow->GetTopBCBorderWidth())); + } + } + // update bottom right corner + BCCornerInfo& brCorner = bottomCorners[colX + segLength]; + brCorner.Update(NS_SIDE_LEFT, owner, ownerBStyle, ownerWidth, ownerColor); + } + } + + // see if the cell to the right had a rowspan and its lower left border needs be joined with this one's bottom + if ((numCols != cellEndColIndex + 1) && // there is a cell to the right + (lastBottomBorders[cellEndColIndex + 1].span > 1)) { // cell to right was a rowspan + BCCornerInfo& corner = bottomCorners[cellEndColIndex + 1]; + if ((NS_SIDE_TOP != corner.ownerSide) && (NS_SIDE_BOTTOM != corner.ownerSide)) { // not a vertical owner + BCCellBorder& thisBorder = lastBottomBorder; + BCCellBorder& nextBorder = lastBottomBorders[info.colIndex + 1]; + if ((thisBorder.color == nextBorder.color) && (thisBorder.width == nextBorder.width) && + (thisBorder.style == nextBorder.style)) { + // set the flag on the next border indicating it is not the start of a new segment + if (iter.mCellMap) { + BCData* bcData = tableCellMap->GetBCData(NS_SIDE_BOTTOM, *iter.mCellMap, cellEndRowIndex, + cellEndColIndex + 1); + if (bcData) { + bcData->SetTopStart(PR_FALSE); + } + } + } + } + } + } // for (iter.First(info); info.cell; iter.Next(info)) { + + // reset the bc flag and damage area + SetNeedToCalcBCBorders(PR_FALSE); + propData->mDamageArea.x = propData->mDamageArea.y = propData->mDamageArea.width = propData->mDamageArea.height = 0; + //mCellMap->Dump(); +} + +// Iterates over borders (left border, corner, top border) in the cell map within a damage area +// from left to right, top to bottom. All members are in terms of the 1st in flow frames, except +// where suffixed by InFlow. +class BCMapBorderIterator +{ +public: + BCMapBorderIterator(nsTableFrame& aTableFrameInFlow, + nsTableRowGroupFrame& aRowGroupFrameInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea); + void Reset(nsTableFrame& aTableFrameInFlow, + nsTableRowGroupFrame& aRowGroupFrameInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea); + void First(); + void Next(); + + nsTableFrame* tableInFlow; + nsTableFrame* table; + nsTableCellMap* tableCellMap; + nsCellMap* cellMap; + + nsVoidArray rowGroups; + nsTableRowGroupFrame* prevRg; + nsTableRowGroupFrame* rgInFlow; + nsTableRowGroupFrame* rg; + PRInt32 rowGroupIndex; + PRInt32 rowGroupStart; + PRInt32 rowGroupEnd; + PRInt32 numRows; + + nsTableRowFrame* prevRow; + nsTableRowFrame* rowInFlow; + nsTableRowFrame* row; + PRInt32 numCols; + PRInt32 x; + PRInt32 y; + + nsTableCellFrame* prevCell; + nsTableCellFrame* cell; + BCCellData* prevCellData; + BCCellData* cellData; + BCData* bcData; + + PRBool IsTopMostTable() { return (y == 0) && !tableInFlow->GetPrevInFlow(); } + PRBool IsRightMostTable() { return (x >= numCols); } + PRBool IsBottomMostTable() { return (y >= numRows) && !tableInFlow->GetNextInFlow(); } + PRBool IsLeftMostTable() { return (x == 0); } + PRBool IsTopMost() { return (y == startY); } + PRBool IsRightMost() { return (x >= endX); } + PRBool IsBottomMost() { return (y >= endY); } + PRBool IsLeftMost() { return (x == startX); } + PRBool isNewRow; + + PRInt32 startX; + PRInt32 startY; + PRInt32 endX; + PRInt32 endY; + PRBool isRepeatedHeader; + PRBool isRepeatedFooter; + PRBool atEnd; + +private: + + PRBool SetNewRow(nsTableRowFrame* aRowInFlow = nsnull, + nsTableRowFrame* aRow = nsnull); + PRBool SetNewRowGroup(PRBool aSetInFlow); + void SetNewData(PRInt32 aY, PRInt32 aX); + +}; + +BCMapBorderIterator::BCMapBorderIterator(nsTableFrame& aTableInFlow, + nsTableRowGroupFrame& aRowGroupInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea) +{ + Reset(aTableInFlow, aRowGroupInFlow, aRowInFlow, aDamageArea); +} + +void +BCMapBorderIterator::Reset(nsTableFrame& aTableInFlow, + nsTableRowGroupFrame& aRowGroupInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea) +{ + atEnd = PR_TRUE; // gets reset when First() is called + + tableInFlow = &aTableInFlow; + table = (nsTableFrame*)aTableInFlow.GetFirstInFlow(); if (!table) ABORT0(); + rgInFlow = &aRowGroupInFlow; + rowInFlow = &aRowInFlow; + + tableCellMap = table->GetCellMap(); + + startX = aDamageArea.x; + startY = aDamageArea.y; + endY = aDamageArea.y + aDamageArea.height; + endX = aDamageArea.x + aDamageArea.width; + + numRows = table->GetRowCount(); + y = 0; + numCols = table->GetColCount(); + x = 0; + rowGroupIndex = -1; + rg = nsnull; + row = nsnull; + cell = nsnull; + cellData = nsnull; + + // Get the ordered row groups + PRUint32 numRowGroups; + table->OrderRowGroups(rowGroups, numRowGroups, nsnull); +} + +void +BCMapBorderIterator::SetNewData(PRInt32 aY, + PRInt32 aX) +{ + if (!tableCellMap || !tableCellMap->mBCInfo) ABORT0(); + + x = aX; + y = aY; + prevCellData = cellData; + if (IsRightMost() && IsBottomMost()) { + cell = nsnull; + bcData = &tableCellMap->mBCInfo->mLowerRightCorner; + } + else if (IsRightMost()) { + cellData = nsnull; + bcData = (BCData*)tableCellMap->mBCInfo->mRightBorders.ElementAt(aY); + } + else if (IsBottomMost()) { + cellData = nsnull; + bcData = (BCData*)tableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); + } + else { + bcData = nsnull; + nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(y - rowGroupStart); + if (row) { + cellData = (row->Count() > x) ? (BCCellData*)row->ElementAt(x) : nsnull; + if (cellData) { + bcData = &cellData->mData; + if (!cellData->IsOrig()) { + if (cellData->IsRowSpan()) { + aY -= cellData->GetRowSpanOffset(); + } + if (cellData->IsColSpan()) { + aX -= cellData->GetColSpanOffset(); + } + if ((aX >= 0) && (aY >= 0)) { + row = (nsVoidArray*)cellMap->mRows.ElementAt(aY - rowGroupStart); + if (row) { + cellData = (BCCellData*)row->ElementAt(aX); + } + } + } + if (cellData->IsOrig()) { + prevCell = cell; + cell = cellData->GetCellFrame(); + } + } + } + } +} + +PRBool +BCMapBorderIterator::SetNewRow(nsTableRowFrame* aRowInFlow, + nsTableRowFrame* aRow) +{ + prevRow = row; + row = (aRow) ? aRow : row->GetNextRow(); + rowInFlow = (aRowInFlow) ? aRowInFlow : rowInFlow->GetNextRow(); + + if (rowInFlow && row) { + isNewRow = PR_TRUE; + y = row->GetRowIndex(); + x = startX; + } + else { + atEnd = PR_TRUE; + } + return !atEnd; +} + + +PRBool +BCMapBorderIterator::SetNewRowGroup(PRBool aSetInFlow) +{ + rowGroupIndex++; + + isRepeatedHeader = PR_FALSE; + isRepeatedFooter = PR_FALSE; + + if (rowGroupIndex < rowGroups.Count()) { + prevRg = rg; + nsIFrame* frame = (nsTableRowGroupFrame*)rowGroups.ElementAt(rowGroupIndex); if (!frame) ABORT1(PR_FALSE); + rg = table->GetRowGroupFrame(frame); if (!rg) ABORT1(PR_FALSE); + rowGroupStart = rg->GetStartRowIndex(); + rowGroupEnd = rowGroupStart + rg->GetRowCount() - 1; + + if (aSetInFlow) { + if (rgInFlow && tableInFlow->GetPrevInFlow()) { + // if there is a prev in flow, then the next rgInFlow is (a) either the previous ones next sibling + // or (b) the rg of the next sibling of the parent if the parent is a scroll frame + nsIFrame* frame = nsnull; + nsIFrame* parent = nsnull; + rgInFlow->GetParent(&parent); + if (parent == tableInFlow) { + rgInFlow->GetNextSibling(&frame); + } + else if (parent) { + parent->GetNextSibling(&frame); + } + rgInFlow = table->GetRowGroupFrame(frame); + if (!rgInFlow) { + atEnd = PR_TRUE; + ABORT1(PR_FALSE); + } + } + else { + rgInFlow = rg; + } + } + if (SetNewRow(rgInFlow->GetFirstRow(), rg->GetFirstRow())) { + cellMap = tableCellMap->GetMapFor(*rg); + } + if (rgInFlow && tableInFlow->GetPrevInFlow() && !rgInFlow->GetPrevInFlow()) { + // if rgInFlow doesn't have a prev in flow, then it may be a repeated header or footer + const nsStyleDisplay* display; + rgInFlow->GetStyleData(eStyleStruct_Display, (const nsStyleStruct *&)display); + if (y == startY) { + isRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); + } + else { + isRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); + } + } + } + else { + atEnd = PR_TRUE; + } + return !atEnd; +} + +void +BCMapBorderIterator::First() +{ + if (!table || (startX >= numCols) || (startY >= numRows)) ABORT0(); + + atEnd = PR_FALSE; + + PRUint32 numRowGroups = rowGroups.Count(); + for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { + nsIFrame* frame = (nsIFrame*)rowGroups.ElementAt(rgX); + nsTableRowGroupFrame* rowG = table->GetRowGroupFrame(frame); + if (rowG) { + PRInt32 start = rowG->GetStartRowIndex(); + PRInt32 end = start + rowG->GetRowCount() - 1; + if ((startY >= start) && (startY <= end)) { + rowGroupIndex = rgX - 1; // SetNewRowGroup increments rowGroupIndex + if (SetNewRowGroup(PR_FALSE)) { + while ((y < startY) && !atEnd) { + SetNewRow(); + } + if (!atEnd) { + SetNewData(startY, startX); + } + } + return; + } + } + } + atEnd = PR_TRUE; +} + +void +BCMapBorderIterator::Next() +{ + if (atEnd) ABORT0(); + isNewRow = PR_FALSE; + + x++; + if (x > endX) { + y++; + if (y == endY) { + x = startX; + } + else if (y < endY) { + if (y <= rowGroupEnd) { + SetNewRow(); + } + else { + SetNewRowGroup(PR_TRUE); + } + } + else { + atEnd = PR_TRUE; + } + } + if (!atEnd) { + SetNewData(y, x); + } +} + +// XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine them +static nscoord +CalcVerCornerOffset(PRUint8 aCornerOwnerSide, + nscoord aCornerSubWidth, + nscoord aHorWidth, + PRBool aIsStartOfSeg, + PRBool aIsBevel, + float aPixelsToTwips) +{ + nscoord offset = 0; + nscoord smallHalf, largeHalf; + if ((NS_SIDE_TOP == aCornerOwnerSide) || (NS_SIDE_BOTTOM == aCornerOwnerSide)) { + DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf; + } + } + else { + DivideBCBorderSize(aHorWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; + } + } + return NSToCoordRound(aPixelsToTwips * (float)offset); +} + +static nscoord +CalcHorCornerOffset(PRUint8 aCornerOwnerSide, + nscoord aCornerSubWidth, + nscoord aVerWidth, + PRBool aIsStartOfSeg, + PRBool aIsBevel, + float aPixelsToTwips) +{ + nscoord offset = 0; + nscoord smallHalf, largeHalf; + if ((NS_SIDE_LEFT == aCornerOwnerSide) || (NS_SIDE_RIGHT == aCornerOwnerSide)) { + DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf; + } + } + else { + DivideBCBorderSize(aVerWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; + } + } + return NSToCoordRound(aPixelsToTwips * (float)offset); +} + +struct BCVerticalSeg +{ + BCVerticalSeg::BCVerticalSeg(); + + void Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + nscoord aVerSegWidth, + nscoord aPrevHorSegHeight, + nscoord aHorSegHeight, + float aPixelsToTwips, + BCVerticalSeg* aVerInfoArray); + + union { + nsTableColFrame* col; + PRInt32 colWidth; + }; + PRInt32 colX; + nsTableCellFrame* ajaCell; + nsTableCellFrame* firstCell; // cell at the start of the segment + nsTableCellFrame* lastCell; // cell at the current end of the segment + PRInt32 segY; + PRInt32 segHeight; + PRInt16 segWidth; // width in pixels + PRUint8 owner; + PRUint8 bevelSide; + PRUint16 bevelOffset; +}; + +BCVerticalSeg::BCVerticalSeg() +{ + col = nsnull; firstCell = lastCell = ajaCell = nsnull; colX = segY = segHeight = 0; + segWidth = bevelOffset = 0; bevelSide = 0; owner = eCellOwner; +} + +void +BCVerticalSeg::Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + nscoord aVerSegWidth, + nscoord aPrevHorSegHeight, + nscoord aHorSegHeight, + float aPixelsToTwips, + BCVerticalSeg* aVerInfoArray) +{ + PRUint8 ownerSide = 0; + PRPackedBool bevel = PR_FALSE; + PRInt32 xAdj = aIter.x - aIter.startX; + + nscoord cornerSubWidth = (aIter.bcData) ? aIter.bcData->GetCorner(ownerSide, bevel) : 0; + PRBool topBevel = (aVerSegWidth > 0) ? bevel : PR_FALSE; + nscoord maxHorSegHeight = PR_MAX(aPrevHorSegHeight, aHorSegHeight); + nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, maxHorSegHeight, + PR_TRUE, topBevel, aPixelsToTwips); + + bevelOffset = (topBevel) ? maxHorSegHeight : 0; + bevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + segY += offset; + segHeight = -offset; + segWidth = aVerSegWidth; + owner = aBorderOwner; + firstCell = aIter.cell; + if (xAdj > 0) { + ajaCell = aVerInfoArray[xAdj - 1].lastCell; + } +} + +struct BCHorizontalSeg +{ + BCHorizontalSeg::BCHorizontalSeg(); + + void Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + PRUint8 aCornerOwnerSide, + nscoord aSubWidth, + PRBool aBevel, + nscoord aTopVerSegWidth, + nscoord aBottomVerSegWidth, + nscoord aHorSegHeight, + nsTableCellFrame* aLastCell, + float aPixelsToTwips); + + nscoord x; + nscoord y; + nscoord width; + nscoord height; + PRBool leftBevel; + nscoord leftBevelOffset; + PRUint8 leftBevelSide; + PRUint8 owner; + nsTableCellFrame* firstCell; // cell at the start of the segment + nsTableCellFrame* ajaCell; +}; + +BCHorizontalSeg::BCHorizontalSeg() +{ + x = y = width = height = leftBevel = leftBevelOffset = leftBevelSide = 0; + firstCell = ajaCell = nsnull; +} + + +void +BCHorizontalSeg::Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + PRUint8 aCornerOwnerSide, + nscoord aSubWidth, + PRBool aBevel, + nscoord aTopVerSegWidth, + nscoord aBottomVerSegWidth, + nscoord aHorSegHeight, + nsTableCellFrame* aLastCell, + float aPixelsToTwips) +{ + owner = aBorderOwner; + leftBevel = (aHorSegHeight > 0) ? aBevel : PR_FALSE; + nscoord maxVerSegWidth = PR_MAX(aTopVerSegWidth, aBottomVerSegWidth); + nscoord offset = CalcHorCornerOffset(aCornerOwnerSide, aSubWidth, maxVerSegWidth, + PR_TRUE, leftBevel, aPixelsToTwips); + leftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; + leftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + x += offset; + width = -offset; + height = aHorSegHeight; + firstCell = aIter.cell; + ajaCell = (aIter.IsTopMost()) ? nsnull : aLastCell; +} + +void +nsTableFrame::PaintBCBorders(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + nsMargin childAreaOffset = GetChildAreaOffset(*aPresContext, nsnull); + nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT0(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + + PRInt32 startColX = childAreaOffset.left; // x position of first col in damage area + PRInt32 startRowY = (mPrevInFlow) ? 0 : childAreaOffset.top; // y position of first row in damage area + + const nsStyleBackground* bgColor = nsStyleUtil::FindNonTransparentBackground(mStyleContext); + // determine the damage area in terms of rows and columns and finalize startColX and startRowY + PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex; + startRowIndex = endRowIndex = startColIndex = endColIndex = 0; + + nsAutoVoidArray rowGroups; + PRUint32 numRowGroups; + OrderRowGroups(rowGroups, numRowGroups, nsnull); + PRBool done = PR_FALSE; + PRBool haveIntersect = PR_FALSE; + nsTableRowGroupFrame* inFlowRG = nsnull; + nsTableRowFrame* inFlowRow = nsnull; + // find startRowIndex, endRowIndex, startRowY + nscoord onePixel = NSToCoordRound(p2t); + PRInt32 rowY = startRowY; + for (PRUint32 rgX = 0; (rgX < numRowGroups) && !done; rgX++) { + nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX); + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0(); + nsRect rowRect; + for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { + // conservatively estimate the half border widths outside the row + nscoord topBorderHalf = (mPrevInFlow) ? 0 : rowFrame->GetTopBCBorderWidth(&p2t) + onePixel; + nscoord bottomBorderHalf = (mNextInFlow) ? 0 : rowFrame->GetBottomBCBorderWidth(&p2t) + onePixel; + // get the row rect relative to the table rather than the row group + rowFrame->GetRect(rowRect); + if (haveIntersect) { + if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { + nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0(); + endRowIndex = fifRow->GetRowIndex(); + } + else done = PR_TRUE; + } + else { + if ((rowY + rowRect.height + bottomBorderHalf) >= aDirtyRect.y) { + inFlowRG = rgFrame; + inFlowRow = rowFrame; + nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0(); + startRowIndex = endRowIndex = fifRow->GetRowIndex(); + haveIntersect = PR_TRUE; + } + else { + startRowY += rowRect.height; + } + } + rowY += rowRect.height; + } + } + if (!inFlowRG || !inFlowRow) ABORT0(); + + // find startColIndex, endColIndex, startColX + haveIntersect = PR_FALSE; + PRUint32 numCols = GetColCount(); + if (0 == numCols) return; + + nscoord x = 0; + for (PRUint32 colX = 0; colX < numCols; colX++) { + nsTableColFrame* colFrame = firstInFlow->GetColFrame(colX); if (!colFrame) ABORT0(); + // conservatively estimate the half border widths outside the col + nscoord leftBorderHalf = colFrame->GetLeftBorderWidth(&p2t) + onePixel; + nscoord rightBorderHalf = colFrame->GetRightBorderWidth(&p2t) + onePixel; + // get the col rect relative to the table rather than the col group + nsRect rect; + colFrame->GetRect(rect); + if (haveIntersect) { + if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { + endColIndex = colX; + } + else break; + } + else { + if ((x + rect.width + rightBorderHalf) >= aDirtyRect.x) { + startColIndex = endColIndex = colX; + haveIntersect = PR_TRUE; + } + else { + startColX += rect.width; + } + } + x += rect.width; + } + + // iterate the cell map and build up border segments + nsRect damageArea(startColIndex, startRowIndex, 1 + endColIndex - startColIndex, + 1 + endRowIndex - startRowIndex); + BCVerticalSeg* verInfo = new BCVerticalSeg[damageArea.width + 1]; if (!verInfo) ABORT0(); + + BCBorderOwner borderOwner, ignoreBorderOwner; + PRUint8 ownerSide; + nscoord cornerSubWidth, smallHalf, largeHalf; + nsRect rowRect(0,0,0,0); + PRBool isSegStart, ignoreSegStart; + nscoord prevHorSegHeight = 0; + PRPackedBool bevel; + PRInt32 repeatedHeaderY = -99; + PRBool afterRepeatedHeader = PR_FALSE; + PRBool startRepeatedFooter = PR_FALSE; + + // First, paint all of the vertical borders from top to bottom and left to right as they become complete + // They are painted first, since they are less efficient to paint than horizontal segments. They were + // stored with as few segments as possible (since horizontal borders are painted last and possibly over them). + BCMapBorderIterator iter(*this, *inFlowRG, *inFlowRow, damageArea); + for (iter.First(); !iter.atEnd; iter.Next()) { + nscoord verSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(borderOwner, isSegStart) : 0; + nscoord horSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0; + + PRInt32 xAdj = iter.x - iter.startX; + if (iter.isNewRow) { + prevHorSegHeight = 0; + iter.rowInFlow->GetRect(rowRect); + if (iter.isRepeatedHeader) { + repeatedHeaderY = iter.y; + } + afterRepeatedHeader = !iter.isRepeatedHeader && (iter.y == (repeatedHeaderY + 1)); + startRepeatedFooter = iter.isRepeatedFooter && (iter.y == iter.rowGroupStart) && (iter.y != iter.startY); + } + BCVerticalSeg& info = verInfo[xAdj]; + if (!info.col) { // on the first damaged row and the first segment in the col + info.col = iter.IsRightMostTable() ? verInfo[xAdj - 1].col : firstInFlow->GetColFrame(iter.x); if (!info.col) ABORT0(); + if (0 == xAdj) { + info.colX = startColX; + } + // set colX for the next column + if (!iter.IsRightMost()) { + nsRect colRect; + info.col->GetRect(colRect); + verInfo[xAdj + 1].colX = info.colX + colRect.width; + } + info.segY = startRowY; + info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, p2t, verInfo); + info.lastCell = iter.cell; + } + + if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost() || afterRepeatedHeader || startRepeatedFooter)) { + // paint the previous seg or the current one if iter.IsBottomMost() + if (info.segHeight > 0) { + cornerSubWidth = (iter.bcData) ? iter.bcData->GetCorner(ownerSide, bevel) : 0; + PRBool endBevel = (info.segWidth > 0) ? bevel : PR_FALSE; + nscoord bottomHorSegHeight = PR_MAX(prevHorSegHeight, horSegHeight); + nscoord endOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, bottomHorSegHeight, + PR_FALSE, endBevel, p2t); + info.segHeight += endOffset; + if (info.segWidth > 0) { + // get the border style, color and paint the segment + PRUint8 side = (iter.IsRightMost()) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + nsTableColGroupFrame* cg = nsnull; + nsTableColFrame* col = info.col; if (!col) ABORT0(); + nsTableCellFrame* cell = info.firstCell; + PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; + nscolor color = 0xFFFFFFFF; + PRBool ignoreIfRules = (iter.IsRightMostTable() || iter.IsLeftMostTable()); + + switch (info.owner) { + case eTableOwner: + ::GetStyleInfo(*this, side, style, color); + break; + case eAjaColGroupOwner: + side = NS_SIDE_RIGHT; + if (!iter.IsRightMostTable() && (xAdj > 0)) { + col = verInfo[xAdj - 1].col; + } // and fall through + case eColGroupOwner: + if (col) { + col->GetParent((nsIFrame**)&cg); + if (cg) { + ::GetStyleInfo(*cg, side, style, color, ignoreIfRules); + } + } + break; + case eAjaColOwner: + side = NS_SIDE_RIGHT; + if (!iter.IsRightMostTable() && (xAdj > 0)) { + col = verInfo[xAdj - 1].col; + } // and fall through + case eColOwner: + if (col) { + ::GetStyleInfo(*col, side, style, color, ignoreIfRules); + } + break; + case eAjaRowGroupOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eRowGroupOwner: + NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error"); + if (iter.rg) { + ::GetStyleInfo(*iter.rg, side, style, color, ignoreIfRules); + } + break; + case eAjaRowOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eRowOwner: + NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error"); + if (iter.row) { + ::GetStyleInfo(*iter.row, side, style, color, ignoreIfRules); + } + break; + case eAjaCellOwner: + side = NS_SIDE_RIGHT; + cell = info.ajaCell; // and fall through + case eCellOwner: + if (cell) { + ::GetStyleInfo(*cell, side, style, color); + } + break; + } + DivideBCBorderSize(info.segWidth, smallHalf, largeHalf); + nsRect segRect(info.colX - NSToCoordRound(p2t * (float)largeHalf), info.segY, + NSToCoordRound(p2t * (float)info.segWidth), info.segHeight); + nscoord bottomBevelOffset = (endBevel) ? NSToCoordRound(p2t * (float)bottomHorSegHeight) : 0; + PRUint8 bottomBevelSide = (horSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, p2t, + info.bevelSide, NSToCoordRound(p2t * (float)info.bevelOffset), + bottomBevelSide, bottomBevelOffset); + } // if (info.segWidth > 0) { + info.segY = info.segY + info.segHeight - endOffset; + } // if (info.segHeight > 0) + info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, p2t, verInfo); + } // if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost())) { + + info.lastCell = iter.cell; + info.segHeight += rowRect.height; + prevHorSegHeight = horSegHeight; + } // for (iter.First(); !iter.atEnd; iter.Next()) { + + // Next, paint all of the horizontal border segments from top to bottom reuse the verInfo + // array to keep tract of col widths and vertical segments for corner calculations + memset(verInfo, 0, damageArea.width * sizeof(BCVerticalSeg)); // XXX reinitialize properly + for (PRInt32 xIndex = 0; xIndex < damageArea.width; xIndex++) { + verInfo[xIndex].colWidth = -1; + } + PRInt32 nextY = startRowY; + BCHorizontalSeg horSeg; + + iter.Reset(*this, *inFlowRG, *inFlowRow, damageArea); + for (iter.First(); !iter.atEnd; iter.Next()) { + nscoord leftSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0; + nscoord topSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(borderOwner, isSegStart) : 0; + + PRInt32 xAdj = iter.x - iter.startX; + // store the current col width if it hasn't been already + if (verInfo[xAdj].colWidth < 0) { + if (iter.IsRightMostTable()) { + verInfo[xAdj].colWidth = verInfo[xAdj - 1].colWidth; + } + else { + nsTableColFrame* col = firstInFlow->GetColFrame(iter.x); if (!col) ABORT0(); + nsRect colRect; + col->GetRect(colRect); + verInfo[xAdj].colWidth = colRect.width; + } + } + cornerSubWidth = (iter.bcData) ? iter.bcData->GetCorner(ownerSide, bevel) : 0; + nscoord verWidth = PR_MAX(verInfo[xAdj].segWidth, leftSegWidth); + if (iter.isNewRow || (iter.IsLeftMost() && iter.IsBottomMostTable())) { + iter.rowInFlow->GetRect(rowRect); + horSeg.y = nextY; + nextY = nextY + rowRect.height; + horSeg.x = startColX; + horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth, + leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, p2t); + } + PRBool verOwnsCorner = (NS_SIDE_TOP == ownerSide) || (NS_SIDE_BOTTOM == ownerSide); + if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) { + // paint the previous seg or the current one if iter.IsRightMost() + if (horSeg.width > 0) { + PRBool endBevel = (horSeg.height > 0) ? bevel : 0; + nscoord endOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, PR_FALSE, endBevel, p2t); + horSeg.width += endOffset; + if (horSeg.height > 0) { + // get the border style, color and paint the segment + PRUint8 side = (iter.IsBottomMost()) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + nsIFrame* rg = iter.rg; if (!rg) ABORT0(); + nsIFrame* row = iter.row; if (!row) ABORT0(); + nsIFrame* cell = horSeg.firstCell; if (!cell) ABORT0(); + nsIFrame* cg; + nsIFrame* col; + + PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; + nscolor color = 0xFFFFFFFF; + PRBool ignoreIfRules = (iter.IsTopMostTable() || iter.IsBottomMostTable()); + + switch (horSeg.owner) { + case eTableOwner: + ::GetStyleInfo(*this, side, style, color); + break; + case eAjaColGroupOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eColGroupOwner: + NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error"); + col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0(); + col->GetParent(&cg); if (!cg) ABORT0(); + ::GetStyleInfo(*cg, side, style, color, ignoreIfRules); + break; + case eAjaColOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eColOwner: + NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error"); + col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0(); + ::GetStyleInfo(*col, side, style, color, ignoreIfRules); + break; + case eAjaRowGroupOwner: + side = NS_SIDE_BOTTOM; + rg = (iter.IsBottomMostTable()) ? iter.rg : iter.prevRg; // and fall through + case eRowGroupOwner: + if (rg) { + ::GetStyleInfo(*rg, side, style, color, ignoreIfRules); + } + break; + case eAjaRowOwner: + side = NS_SIDE_BOTTOM; + row = iter.prevRow; // and fall through + case eRowOwner: + if (row) { + ::GetStyleInfo(*row, side, style, color, iter.IsBottomMostTable()); + } + break; + case eAjaCellOwner: + side = NS_SIDE_BOTTOM; + // if this is null due to the damage area origin-y > 0, then the border won't show up anyway + cell = horSeg.ajaCell; + // and fall through + case eCellOwner: + if (cell) { + ::GetStyleInfo(*cell, side, style, color); + } + break; + } + DivideBCBorderSize(horSeg.height, smallHalf, largeHalf); + nsRect segRec(horSeg.x, horSeg.y - NSToCoordRound(p2t * (float)largeHalf), horSeg.width, + NSToCoordRound(p2t * (float)horSeg.height)); + nscoord rightBevelOffset = (endBevel) ? NSToCoordRound(p2t * (float)verWidth) : 0; + PRUint8 rightBevelSide = (leftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRec, p2t, horSeg.leftBevelSide, + NSToCoordRound(p2t * (float)horSeg.leftBevelOffset), + rightBevelSide, rightBevelOffset); + } // if (horSeg.height > 0) { + horSeg.x = horSeg.x + horSeg.width - endOffset; + } // if (horSeg.width > 0) { + horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth, + leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, p2t); + } // if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) { + horSeg.width += verInfo[xAdj].colWidth; + verInfo[xAdj].segWidth = leftSegWidth; + verInfo[xAdj].lastCell = iter.cell; + } + delete [] verInfo; +} /******************************************************************************** ** DEBUG_TABLE_REFLOW and DEBUG_TABLE_REFLOW_TIMING ** @@ -4579,7 +6993,7 @@ GetFrameTypeName(nsIAtom* aFrameType, strcpy(aName, "RowG"); else if (nsLayoutAtoms::tableRowFrame == aFrameType) strcpy(aName, "Row"); - else if (nsLayoutAtoms::tableCellFrame == aFrameType) + else if (IS_TABLE_CELL(aFrameType)) strcpy(aName, "Cell"); else if (nsLayoutAtoms::blockFrame == aFrameType) strcpy(aName, "Block"); @@ -4621,7 +7035,7 @@ void DebugGetIndent(const nsIFrame* aFrame, (nsLayoutAtoms::tableFrame == frameType.get()) || (nsLayoutAtoms::tableRowGroupFrame == frameType.get()) || (nsLayoutAtoms::tableRowFrame == frameType.get()) || - (nsLayoutAtoms::tableCellFrame == frameType.get())) { + IS_TABLE_CELL(frameType.get())) { numLevels++; } if (nsLayoutAtoms::blockFrame == frameType.get()) { @@ -4630,7 +7044,7 @@ void DebugGetIndent(const nsIFrame* aFrame, parent->GetParent(&grandParent); nsCOMPtr gFrameType; grandParent->GetFrameType(getter_AddRefs(gFrameType)); - if (nsLayoutAtoms::tableCellFrame == gFrameType.get()) { + if (IS_TABLE_CELL(gFrameType.get())) { numLevels++; } } @@ -4715,14 +7129,14 @@ nsReflowTimer* GetFrameTimer(nsIFrame* aFrame, return ((nsTableRowGroupFrame*)aFrame)->mTimer; else if (nsLayoutAtoms::tableRowFrame == aFrameType) return ((nsTableRowFrame*)aFrame)->mTimer; - else if (nsLayoutAtoms::tableCellFrame == aFrameType) + else if (IS_TABLE_CELL(aFrameType)) return ((nsTableCellFrame*)aFrame)->mTimer; else if (nsLayoutAtoms::blockFrame == aFrameType) { nsIFrame* parentFrame; aFrame->GetParent(&parentFrame); nsCOMPtr fType; parentFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType) { + if (IS_TABLE_CELL(fType)) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)parentFrame; // fix up the block timer, which may be referring to the cell if (cellFrame->mBlockTimer->mFrame == parentFrame) { @@ -4967,7 +7381,7 @@ void nsTableFrame::DebugReflowDone(nsIFrame* aFrame) ancestorTimer->mChildren.AppendElement(thisTimer); nsCOMPtr fType; aFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType) { + if (IS_TABLE_CELL(fType)) { // add the cell block timer as a child of the cell timer nsTableCellFrame* cellFrame = (nsTableCellFrame*)aFrame; cellFrame->mTimer->mChildren.AppendElement(cellFrame->mBlockTimer); @@ -5056,6 +7470,16 @@ DestroyPointFunc(nsIPresContext* aPresContext, delete (nsPoint*)aPropertyValue; } +// Destructor function for nscoord properties +static void +DestroyBCPropertyDataFunc(nsIPresContext* aPresContext, + nsIFrame* aFrame, + nsIAtom* aPropertyName, + void* aPropertyValue) +{ + delete (BCPropertyData*)aPropertyValue; +} + void* nsTableFrame::GetProperty(nsIPresContext* aPresContext, nsIFrame* aFrame, @@ -5089,6 +7513,10 @@ nsTableFrame::GetProperty(nsIPresContext* aPresContext, value = new nscoord; dtorFunc = DestroyCoordFunc; } + else if (aPropertyName == nsLayoutAtoms::tableBCProperty) { + value = new BCPropertyData; + dtorFunc = DestroyBCPropertyDataFunc; + } if (!value) return nsnull; frameManager->SetFrameProperty(aFrame, aPropertyName, value, dtorFunc); @@ -5160,7 +7588,7 @@ void DumpTableFramesRecur(nsIPresContext* aPresContext, if (nsLayoutAtoms::tableFrame == fType.get() || nsLayoutAtoms::tableRowGroupFrame == fType.get() || nsLayoutAtoms::tableRowFrame == fType.get() || - nsLayoutAtoms::tableCellFrame == fType.get()) { + IS_TABLE_CELL(fType.get())) { nsIFrame* child; aFrame->FirstChild(aPresContext, nsnull, &child); while(child) { diff --git a/mozilla/layout/html/table/src/nsTableFrame.h b/mozilla/layout/html/table/src/nsTableFrame.h index 4862e4a92c2..7dc64be49b4 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.h +++ b/mozilla/layout/html/table/src/nsTableFrame.h @@ -53,7 +53,6 @@ class nsTableColFrame; class nsTableRowGroupFrame; class nsTableRowFrame; class nsTableColGroupFrame; -class nsTableBorderCollapser; class nsITableLayoutStrategy; class nsHTMLValue; @@ -208,6 +207,8 @@ public: nsIAtom* aPropertyName, PRBool aCreateIfNecessary = PR_FALSE); + static float GetTwipsToPixels(nsIPresContext* aPresContext); + static nscoord RoundToPixel(nscoord aValue, float aPixelToTwips, nsPixelRound aRound= eAlwaysRoundUp); @@ -249,7 +250,13 @@ public: nsIAtom* aListName, nsIFrame* aOldFrame); - nsMargin GetBorderPadding(const nsHTMLReflowState& aReflowState) const; + // Get the offset from the border box to the area where the row groups fit + nsMargin GetChildAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const; + + // Get the offset from the border box to the area where the content fits + nsMargin GetContentAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const; /** helper method to find the table parent of any table frame object */ // TODO: today, this depends on display types. This should be changed to rely @@ -297,6 +304,17 @@ public: nsFramePaintLayer aWhichLayer, PRUint32 aFlags = 0); + nsMargin* GetBCBorder(nsIPresContext& aPresContext, + PRBool aInnerBorderOnly, + nsMargin& aBorder) const; + + void SetBCDamageArea(nsIPresContext& aPresContext, + const nsRect& aValue); + + void PaintBCBorders(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD GetFrameForPoint(nsIPresContext* aPresContext, const nsPoint& aPoint, nsFramePaintLayer aWhichLayer, @@ -371,15 +389,12 @@ public: /** set the width of the column at aColIndex to aWidth */ virtual void SetColumnWidth(PRInt32 aColIndex, nscoord aWidth); - /** helper to get the border collapse style value */ - virtual PRUint8 GetBorderCollapseStyle(); - /** helper to get the cell spacing X style value */ virtual nscoord GetCellSpacingX(); /** helper to get the cell spacing Y style value */ virtual nscoord GetCellSpacingY(); - + /** return the row span of a cell, taking into account row span magic at the bottom * of a table. The row span equals the number of rows spanned by aCell starting at * aStartRowIndex, and can be smaller if aStartRowIndex is greater than the row @@ -440,9 +455,9 @@ public: /** empty the column frame cache */ void ClearColCache(); - virtual PRInt32 AppendCell(nsIPresContext& aPresContext, - nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex); + virtual void AppendCell(nsIPresContext& aPresContext, + nsTableCellFrame& aCellFrame, + PRInt32 aRowIndex); virtual void InsertCells(nsIPresContext& aPresContext, nsVoidArray& aCellFrames, @@ -522,6 +537,9 @@ protected: /** destructor, responsible for mColumnLayoutData */ virtual ~nsTableFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState); + /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -619,7 +637,8 @@ protected: nsIFrame* aKidFrame, nscoord aDeltaY); - nsresult RecoverState(nsTableReflowState& aReflowState, + nsresult RecoverState(nsIPresContext& aPresContext, + nsTableReflowState& aReflowState, nsIFrame* aKidFrame); NS_METHOD CollapseRowGroupIfNecessary(nsIPresContext* aPresContext, @@ -646,10 +665,11 @@ public: // calculate the computed height of aFrame including its border and padding given // its reflow state. - nscoord CalcBorderBoxHeight(const nsHTMLReflowState& aReflowState); + nscoord CalcBorderBoxHeight(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState); // calculate the minimum width to layout aFrame and its desired width // including border and padding given its reflow state and column width information - void CalcMinAndPreferredWidths(nsIPresContext* aPresContextconst, + void CalcMinAndPreferredWidths(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState, PRBool aCalcPrefWidthIfAutoWithPctCol, nscoord& aMinWidth, @@ -657,7 +677,8 @@ public: protected: // calcs the width of the table according to the computed widths of each column. - virtual PRInt32 CalcDesiredWidth(const nsHTMLReflowState& aReflowState); + virtual PRInt32 CalcDesiredWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState); // return the desired height of this table accounting for the current // reflow state, and for the table attributes and parent @@ -699,7 +720,7 @@ public: PRUint32& aNumRowGroups, nsIFrame** aFirstBody = nsnull, nsTableRowGroupFrame** aHead = nsnull, - nsTableRowGroupFrame** aFoot = nsnull); + nsTableRowGroupFrame** aFoot = nsnull) const; // Returns PR_TRUE if there are any cells above the row at // aRowIndex and spanning into the row at aRowIndex @@ -735,9 +756,6 @@ protected: PRBool DidResizeReflow() const; void SetResizeReflow(PRBool aValue); - /** Support methods for DidSetStyleContext */ - void MapBorderMarginPadding(nsIPresContext* aPresContext); - void MapHTMLBorderStyle(nsStyleBorder& aBorderStyle, nscoord aBorderWidth); PRBool ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult); @@ -749,6 +767,11 @@ public: PRBool NeedStrategyBalance() const; void SetNeedStrategyBalance(PRBool aValue); + PRBool IsBorderCollapse() const; + + PRBool NeedToCalcBCBorders() const; + void SetNeedToCalcBCBorders(PRBool aValue); + /** Get the cell map for this table frame. It is not always mCellMap. * Only the firstInFlow has a legit cell map */ @@ -763,9 +786,6 @@ public: PRInt32 aRowIndex, PRInt32 anAdjustment); - // Return PR_TRUE if rules=groups is set for the table content - PRBool HasGroupRules() const; - // Remove cell borders which aren't bordering row and/or col groups void ProcessGroupRules(nsIPresContext* aPresContext); @@ -775,10 +795,16 @@ public: * Return aFrame's child if aFrame is an nsScrollFrame, otherwise return aFrame */ nsTableRowGroupFrame* GetRowGroupFrame(nsIFrame* aFrame, - nsIAtom* aFrameTypeIn = nsnull); + nsIAtom* aFrameTypeIn = nsnull) const; protected: + void SetBorderCollapse(PRBool aValue); + + void CalcBCBorders(nsIPresContext& aPresContext); + + void ExpandBCDamageArea(nsRect& aRect) const; + PRBool HadInitialReflow() const; void SetHadInitialReflow(PRBool aValue); @@ -809,8 +835,8 @@ public: /* ----- Cell Map public methods ----- */ /** returns the number of columns in this table after redundant columns have been removed */ - virtual PRInt32 GetEffectiveColCount(); - virtual PRInt32 GetColCount(); + virtual PRInt32 GetEffectiveColCount() const; + virtual PRInt32 GetColCount() const; /** return the column frame at colIndex. * returns nsnull if the col frame has not yet been allocated, or if aColIndex is out of range @@ -886,6 +912,7 @@ protected: unsigned mHasPctCol:1; // does any cell or col have a pct width unsigned mCellSpansPctCol:1; // does any cell span a col with a pct width (or containing a cell with a pct width) unsigned mDidResizeReflow:1; // did a resize reflow happen (indicating pass 2) + unsigned mIsBorderCollapse:1; // border collapsing model vs. separate model // true if a descendant was reflowed normally since the last time we reflowed. // We will likely need a timeout reflow (targeted either at us or below) unsigned mDescendantReflowedNotTimeout:1; @@ -896,7 +923,8 @@ protected: unsigned mRowInserted:1; unsigned mNeedSpecialReflow:1; unsigned mNeedToInitiateSpecialReflow:1; - unsigned : 20; // unused + unsigned mNeedToCalcBCBorders:1; + unsigned : 18; // unused } mBits; nsTableCellMap* mCellMap; // maintains the relationships between rows, cols, and cells @@ -1054,8 +1082,26 @@ inline void nsTableFrame::SetPreferredWidth(nscoord aWidth) mPreferredWidth = aWidth; } +inline PRBool nsTableFrame::IsBorderCollapse() const +{ + return (PRBool)mBits.mIsBorderCollapse; +} + +inline void nsTableFrame::SetBorderCollapse(PRBool aValue) +{ + mBits.mIsBorderCollapse = aValue; +} + +inline PRBool nsTableFrame::NeedToCalcBCBorders() const +{ + return (PRBool)mBits.mNeedToCalcBCBorders; +} + +inline void nsTableFrame::SetNeedToCalcBCBorders(PRBool aValue) +{ + mBits.mNeedToCalcBCBorders = (unsigned)aValue; +} - enum nsTableIteration { eTableLTR = 0, eTableRTL = 1, @@ -1085,6 +1131,23 @@ protected: PRInt32 mCount; }; +#define ABORT0() \ +{NS_ASSERTION(PR_FALSE, "CellIterator program error"); \ +return;} + +#define ABORT1(aReturn) \ +{NS_ASSERTION(PR_FALSE, "CellIterator program error"); \ +return aReturn;} + +#define GET_PIXELS_TO_TWIPS(presContext,var) \ + float var; \ + (presContext)->GetScaledPixelsToTwips(&var); + +#define GET_TWIPS_TO_PIXELS(presContext,var) \ + float var; \ + (presContext)->GetScaledPixelsToTwips(&var); \ + var = 1.0f / var; + #endif diff --git a/mozilla/layout/html/table/src/nsTableOuterFrame.cpp b/mozilla/layout/html/table/src/nsTableOuterFrame.cpp index cd77fdd51e4..d402caa1396 100644 --- a/mozilla/layout/html/table/src/nsTableOuterFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableOuterFrame.cpp @@ -232,7 +232,14 @@ nsTableOuterFrame::SetInitialChildList(nsIPresContext* aPresContext, } else { mFrames.SetFrames(aChildList); - mInnerTableFrame = aChildList; + mInnerTableFrame = nsnull; + if (aChildList) { + nsCOMPtr fType; + aChildList->GetFrameType(getter_AddRefs(fType)); + if (nsLayoutAtoms::tableFrame == fType.get()) { + mInnerTableFrame = (nsTableFrame*)aChildList; + } + } } return NS_OK; @@ -466,21 +473,46 @@ FixAutoMargins(nscoord aAvailWidth, } } +void +nsTableOuterFrame::InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState) + +{ + nsMargin collapseBorder; + nsMargin collapsePadding(0,0,0,0); + nsMargin* pCollapseBorder = nsnull; + nsMargin* pCollapsePadding = nsnull; + if ((aReflowState.frame == mInnerTableFrame) && (mInnerTableFrame->IsBorderCollapse())) { + if (mInnerTableFrame->NeedToCalcBCBorders()) { + mInnerTableFrame->CalcBCBorders(aPresContext); + } + pCollapseBorder = mInnerTableFrame->GetBCBorder(aPresContext, PR_FALSE, collapseBorder); + pCollapsePadding = &collapsePadding; + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding); +} + // get the margin and padding data. nsHTMLReflowState doesn't handle the // case of auto margins void -GetMarginPadding(nsIPresContext* aPresContext, - const nsHTMLReflowState& aOuterRS, - nsIFrame* aChildFrame, - nsMargin& aMargin, - nsMargin& aMarginNoAuto, - nsMargin& aPadding) +nsTableOuterFrame::GetMarginPadding(nsIPresContext* aPresContext, + const nsHTMLReflowState& aOuterRS, + nsIFrame* aChildFrame, + nsMargin& aMargin, + nsMargin& aMarginNoAuto, + nsMargin& aPadding) { + if (!aPresContext) ABORT0(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); // construct a reflow state to compute margin and padding. Auto margins // will not be computed at this time. + + // create an init the child reflow state nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, nsSize(aOuterRS.availableWidth, aOuterRS.availableHeight), - eReflowReason_Resize); + eReflowReason_Resize, PR_FALSE); + InitChildReflowState(*aPresContext, childRS); + nsRect childRect; aChildFrame->GetRect(childRect); FixAutoMargins(aOuterRS.availableWidth, childRect.width, childRS); @@ -931,6 +963,7 @@ nsTableOuterFrame::OuterReflowChild(nsIPresContext* aPresContext, nsReflowReason aReflowReason, nsReflowStatus& aStatus) { + if (!aPresContext) ABORT1(NS_ERROR_NULL_POINTER); aMargin = aPadding = nsMargin(0,0,0,0); nscoord availWidth = GetChildAvailWidth(aPresContext, aChildFrame, aOuterRS, @@ -946,9 +979,10 @@ nsTableOuterFrame::OuterReflowChild(nsIPresContext* aPresContext, availHeight = nsTableFrame::RoundToPixel(availHeight, p2t, eAlwaysRoundDown); } - nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, - nsSize(availWidth, availHeight)); - childRS.reason = aReflowReason; + // create and init the child reflow state + nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, nsSize(availWidth, availHeight), + aReflowReason); + InitChildReflowState(*aPresContext, childRS); childRS.mPercentHeightObserver = nsnull; // the observer is for non table related frames inside cells diff --git a/mozilla/layout/html/table/src/nsTableOuterFrame.h b/mozilla/layout/html/table/src/nsTableOuterFrame.h index ee689ab6f08..16eaba4c89d 100644 --- a/mozilla/layout/html/table/src/nsTableOuterFrame.h +++ b/mozilla/layout/html/table/src/nsTableOuterFrame.h @@ -47,6 +47,7 @@ class nsReflowTimer; #endif struct nsStyleTable; +class nsTableFrame; class nsTableCaptionFrame : public nsBlockFrame { @@ -201,6 +202,9 @@ protected: nsTableOuterFrame(); virtual ~nsTableOuterFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState); + /** Always returns 0, since the outer table frame has no border of its own * The inner table frame can answer this question in a meaningful way. * @see nsHTMLContainerFrame::GetSkipSides */ @@ -362,11 +366,17 @@ protected: PRBool aInnerChanged, PRBool aCaptionChanged); + void GetMarginPadding(nsIPresContext* aPresContext, + const nsHTMLReflowState& aOuterRS, + nsIFrame* aChildFrame, + nsMargin& aMargin, + nsMargin& aMarginNoAuto, + nsMargin& aPadding); private: // used to keep track of this frame's children. They are redundant with mFrames, but more convient - nsIFrame* mInnerTableFrame; - nsIFrame* mCaptionFrame; + nsTableFrame* mInnerTableFrame; + nsIFrame* mCaptionFrame; // used to track caption max element size PRInt32 mMinCaptionWidth; diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.cpp b/mozilla/layout/html/table/src/nsTableRowFrame.cpp index 59055548c3f..48687f0efb5 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowFrame.cpp @@ -68,11 +68,6 @@ struct nsTableCellReflowState : public nsHTMLReflowState const nsSize& aAvailableSpace, nsReflowReason aReason); - nsTableCellReflowState(nsIPresContext* aPresContext, - const nsHTMLReflowState& aParentReflowState, - nsIFrame* aFrame, - const nsSize& aAvailableSpace); - void FixUp(const nsSize& aAvailSpace); }; @@ -83,16 +78,43 @@ nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresCon nsReflowReason aReason) :nsHTMLReflowState(aPresContext, aParentRS, aFrame, aAvailSpace, aReason) { - FixUp(aAvailSpace); } -nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresContext, - const nsHTMLReflowState& aParentRS, - nsIFrame* aFrame, - const nsSize& aAvailSpace) - :nsHTMLReflowState(aPresContext, aParentRS, aFrame, aAvailSpace) +void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) { - FixUp(aAvailSpace); + // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base + if (NS_UNCONSTRAINEDSIZE != aAvailSpace.width) { + if (NS_UNCONSTRAINEDSIZE != mComputedWidth) { + mComputedWidth = aAvailSpace.width - mComputedBorderPadding.left - mComputedBorderPadding.right; + mComputedWidth = PR_MAX(0, mComputedWidth); + } + if (NS_UNCONSTRAINEDSIZE != mComputedHeight) { + if (NS_UNCONSTRAINEDSIZE != aAvailSpace.height) { + mComputedHeight = aAvailSpace.height - mComputedBorderPadding.top - mComputedBorderPadding.bottom; + mComputedHeight = PR_MAX(0, mComputedHeight); + } + } + } +} + +void +nsTableRowFrame::InitChildReflowState(nsIPresContext& aPresContext, + const nsSize& aAvailSize, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsTableCellReflowState& aReflowState) +{ + nsMargin collapseBorder; + nsMargin* pCollapseBorder = nsnull; + if (aBorderCollapse) { + // we only reflow cells, so don't need to check frame type + nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame; + if (bcCellFrame) { + pCollapseBorder = bcCellFrame->GetBorderWidth(aPixelsToTwips, collapseBorder); + } + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder); + aReflowState.FixUp(aAvailSize); } void @@ -132,23 +154,6 @@ nsTableRowFrame::SetPctHeight(float aPctValue, } } -void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) -{ - // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base - if (NS_UNCONSTRAINEDSIZE != aAvailSpace.width) { - if (NS_UNCONSTRAINEDSIZE != mComputedWidth) { - mComputedWidth = aAvailSpace.width - mComputedBorderPadding.left - mComputedBorderPadding.right; - mComputedWidth = PR_MAX(0, mComputedWidth); - } - if (NS_UNCONSTRAINEDSIZE != mComputedHeight) { - if (NS_UNCONSTRAINEDSIZE != aAvailSpace.height) { - mComputedHeight = aAvailSpace.height - mComputedBorderPadding.top - mComputedBorderPadding.bottom; - mComputedHeight = PR_MAX(0, mComputedHeight); - } - } - } -} - // 'old' is old cached cell's desired size // 'new' is new cell's size including style constraints static PRBool @@ -217,7 +222,7 @@ nsTableRowFrame::SetInitialChildList(nsIPresContext* aPresContext, kidFrame->GetNextSibling(&kidFrame)) { nsCOMPtr frameType; kidFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { if (((nsTableCellFrame*)kidFrame)->GetRowSpan() > 1) { mState |= NS_FRAME_OUTSIDE_CHILDREN; } @@ -241,7 +246,7 @@ nsTableRowFrame::AppendFrames(nsIPresContext* aPresContext, for (nsIFrame* childFrame = aFrameList; childFrame; childFrame->GetNextSibling(&childFrame)) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { // Add the cell to the cell map tableFrame->AppendCell(*aPresContext, (nsTableCellFrame&)*childFrame, GetRowIndex()); // XXX this could be optimized with some effort @@ -272,12 +277,13 @@ nsTableRowFrame::InsertFrames(nsIPresContext* aPresContext, nsTableFrame::GetTableFrame(this, tableFrame); // gather the new frames (only those which are cells) into an array - nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(aPresContext, this, aPrevFrame, nsLayoutAtoms::tableCellFrame); + nsIAtom* cellFrameType = (tableFrame->IsBorderCollapse()) ? nsLayoutAtoms::bcTableCellFrame : nsLayoutAtoms::tableCellFrame; + nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(aPresContext, this, aPrevFrame, cellFrameType); nsVoidArray cellChildren; for (nsIFrame* childFrame = aFrameList; childFrame; childFrame->GetNextSibling(&childFrame)) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { cellChildren.AppendElement(childFrame); // XXX this could be optimized with some effort tableFrame->SetNeedStrategyInit(PR_TRUE); @@ -315,7 +321,7 @@ nsTableRowFrame::RemoveFrame(nsIPresContext* aPresContext, if (tableFrame) { nsCOMPtr frameType; aOldFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)aOldFrame; PRInt32 colIndex; cellFrame->GetColIndex(colIndex); @@ -372,7 +378,7 @@ nsTableRowFrame::GetFirstCell() while (childFrame) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { return (nsTableCellFrame*)childFrame; } childFrame->GetNextSibling(&childFrame); @@ -398,7 +404,7 @@ nsTableRowFrame::DidResize(nsIPresContext* aPresContext, while (childFrame) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)childFrame; nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame); @@ -534,7 +540,7 @@ nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame->GetNextSibling(&kidFrame)) { nsCOMPtr frameType; kidFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nscoord availWidth = ((nsTableCellFrame *)kidFrame)->GetPriorAvailWidth(); nsSize desSize = ((nsTableCellFrame *)kidFrame)->GetDesiredSize(); if ((NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) && !mPrevInFlow) { @@ -888,28 +894,29 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, aStatus = NS_FRAME_COMPLETE; if (!mFrames.FirstChild()) return NS_OK; - nsTableFrame* tableFrame = &aTableFrame; - if (!tableFrame) return NS_ERROR_NULL_POINTER; + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + PRBool borderCollapse = (((nsTableFrame*)aTableFrame.GetFirstInFlow())->IsBorderCollapse()); + nsIFrame* tablePrevInFlow; - tableFrame->GetPrevInFlow(&tablePrevInFlow); + aTableFrame.GetPrevInFlow(&tablePrevInFlow); PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); nsresult rv = NS_OK; - nscoord cellSpacingX = tableFrame->GetCellSpacingX(); + nscoord cellSpacingX = aTableFrame.GetCellSpacingX(); PRInt32 cellColSpan = 1; // must be defined here so it's set properly for non-cell kids nsTableIteration dir = (aReflowState.availableWidth == NS_UNCONSTRAINEDSIZE) ? eTableLTR : eTableDIR; nsTableIterator iter(aPresContext, *this, dir); // remember the col index of the previous cell to handle rowspans into this row - PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : tableFrame->GetColCount(); + PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount(); PRInt32 prevColIndex = firstPrevColIndex; nscoord x = 0; // running total of children x offset - nsTableFrame* tableFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); - PRBool isAutoLayout = tableFrame->IsAutoLayout(); + nsTableFrame* tableFirstInFlow = (nsTableFrame*)aTableFrame.GetFirstInFlow(); + PRBool isAutoLayout = aTableFrame.IsAutoLayout(); PRBool needToNotifyTable = PR_TRUE; nscoord paginatedHeight = 0; // If the incremental reflow command is a StyleChanged reflow and @@ -945,7 +952,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, doReflowChild = PR_FALSE; } if (aReflowState.mFlags.mSpecialHeightReflow) { - if (!isPaginated && (nsLayoutAtoms::tableCellFrame == frameType.get() && + if (!isPaginated && (IS_TABLE_CELL(frameType.get()) && !((nsTableCellFrame*)kidFrame)->NeedSpecialReflow())) { kidFrame = iter.Next(); continue; @@ -954,22 +961,22 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // Reflow the child frame if (doReflowChild) { - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)kidFrame; PRInt32 cellColIndex; cellFrame->GetColIndex(cellColIndex); - cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame); + cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame); x += cellSpacingX; // If the adjacent cell is in a prior row (because of a rowspan) add in the space if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) { - x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, *tableFrame, + x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, cellSpacingX, iter.IsLeftToRight()); } // Calculate the available width for the table cell using the known column widths nscoord availColWidth, availCellWidth; - CalcAvailWidth(*tableFrame, GetComputedWidth(aReflowState, *tableFrame), + CalcAvailWidth(aTableFrame, GetComputedWidth(aReflowState, aTableFrame), *cellFrame, cellSpacingX, availColWidth, availCellWidth); if (0 == availColWidth) availColWidth = NS_UNCONSTRAINEDSIZE; if (0 == availCellWidth) availCellWidth = NS_UNCONSTRAINEDSIZE; @@ -1032,6 +1039,8 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // Reflow the child nsTableCellReflowState kidReflowState(aPresContext, aReflowState, kidFrame, kidAvailSize, reason); + InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse, p2t, kidReflowState); + nsReflowStatus status; rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, x, 0, 0, status); @@ -1052,7 +1061,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // allow the table to determine if/how the table needs to be rebalanced if (cellToWatch && needToNotifyTable) { - needToNotifyTable = !tableFrame->CellChangedWidth(*cellFrame, oldMaxWidth, oldMaxElemWidth); + needToNotifyTable = !aTableFrame.CellChangedWidth(*cellFrame, oldMaxWidth, oldMaxElemWidth); } // If any of the cells are not complete, then we're not complete @@ -1081,10 +1090,10 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // height may have changed, adjust descent to absorb any excess difference nscoord ascent = cellFrame->GetDesiredAscent(); nscoord descent = desiredSize.height - ascent; - UpdateHeight(desiredSize.height, ascent, descent, tableFrame, cellFrame); + UpdateHeight(desiredSize.height, ascent, descent, &aTableFrame, cellFrame); } else { - PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); + PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); if ((1 == rowSpan) && (desiredSize.height > paginatedHeight)) { paginatedHeight = desiredSize.height; SetContentHeight(paginatedHeight); @@ -1102,13 +1111,14 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, else {// it's an unknown frame type, give it a generic reflow and ignore the results nsTableCellReflowState kidReflowState(aPresContext, aReflowState, kidFrame, nsSize(0,0), eReflowReason_Resize); + InitChildReflowState(*aPresContext, nsSize(0,0), PR_FALSE, p2t, kidReflowState); nsHTMLReflowMetrics desiredSize(nsnull); nsReflowStatus status; ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status); kidFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED); } } - else if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + else if (IS_TABLE_CELL(frameType.get())) { // we need to account for the cell's width even if it isn't reflowed nsRect rect; kidFrame->GetRect(rect); @@ -1244,7 +1254,10 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, // update its maximum width. nsHTMLReflowMetrics cellMet(&kidMaxElementSize, isAutoLayout ? NS_REFLOW_CALC_MAX_WIDTH : 0); - nsTableCellReflowState kidRS(aPresContext, aReflowState, aNextFrame, cellAvailSize); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + nsTableCellReflowState kidRS(aPresContext, aReflowState, aNextFrame, cellAvailSize, + aReflowState.reason); + InitChildReflowState(*aPresContext, cellAvailSize, aTableFrame.IsBorderCollapse(), p2t, kidRS); // Remember the current desired size, we'll need it later nscoord oldCellMinWidth = cellFrame->GetPass1MaxElementWidth(); @@ -1492,13 +1505,20 @@ nsTableRowFrame::ReflowCellFrame(nsIPresContext* aPresContext, nscoord aAvailableHeight, nsReflowStatus& aStatus) { + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(0); + // Reflow the cell frame with the specified height. Use the existing width nsSize cellSize; aCellFrame->GetSize(cellSize); nsSize availSize(cellSize.width, aAvailableHeight); + PRBool borderCollapse = ((nsTableFrame*)tableFrame->GetFirstInFlow())->IsBorderCollapse(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); nsTableCellReflowState cellReflowState(aPresContext, aReflowState, aCellFrame, availSize, eReflowReason_Resize); + InitChildReflowState(*aPresContext, availSize, borderCollapse, p2t, cellReflowState); + nsHTMLReflowMetrics desiredSize(nsnull); ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState, @@ -1534,7 +1554,7 @@ nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame, for (nsIFrame* child = mFrames.FirstChild(); child; child->GetNextSibling(&child)) { nsCOMPtr frameType; child->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame != frameType.get()) { + if (!IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)child; PRInt32 colIndex; cellFrame->GetColIndex(colIndex); diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.h b/mozilla/layout/html/table/src/nsTableRowFrame.h index 552ee7910b3..d05dc5eab6a 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.h +++ b/mozilla/layout/html/table/src/nsTableRowFrame.h @@ -42,6 +42,7 @@ class nsTableFrame; class nsTableCellFrame; +struct nsTableCellReflowState; #ifdef DEBUG_TABLE_REFLOW_TIMING class nsReflowTimer; @@ -240,6 +241,12 @@ public: nscoord GetUnpaginatedHeight(nsIPresContext* aPresContext); void SetUnpaginatedHeight(nsIPresContext* aPresContext, nscoord aValue); + nscoord GetTopBCBorderWidth(float* aPixelsToTwips = nsnull); + void SetTopBCBorderWidth(nscoord aWidth); + nscoord GetBottomBCBorderWidth(float* aPixelsToTwips = nsnull); + void SetBottomBCBorderWidth(nscoord aWidth); + nsMargin* GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder); protected: /** protected constructor. @@ -247,6 +254,12 @@ protected: */ nsTableRowFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + const nsSize& aAvailSize, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsTableCellReflowState& aReflowState); + /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -315,6 +328,10 @@ private: nscoord mMaxCellAscent; // does include cells with rowspan > 1 nscoord mMaxCellDescent; // does *not* include cells with rowspan > 1 + // border widths in pixels in the collapsing border model + unsigned mTopBorderWidth:8; + unsigned mBottomBorderWidth:8; + #ifdef DEBUG_TABLE_REFLOW_TIMING public: nsReflowTimer* mTimer; @@ -421,4 +438,37 @@ inline void nsTableRowFrame::SetHasUnpaginatedHeight(PRBool aValue) } } +inline nscoord nsTableRowFrame::GetTopBCBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mTopBorderWidth) : mTopBorderWidth; + return width; +} + +inline void nsTableRowFrame::SetTopBCBorderWidth(nscoord aWidth) +{ + mTopBorderWidth = aWidth; +} + +inline nscoord nsTableRowFrame::GetBottomBCBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mBottomBorderWidth) : mBottomBorderWidth; + return width; +} + +inline void nsTableRowFrame::SetBottomBCBorderWidth(nscoord aWidth) +{ + mBottomBorderWidth = aWidth; +} + +inline nsMargin* nsTableRowFrame::GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) +{ + aBorder.left = aBorder.right = 0; + + aBorder.top = NSToCoordRound(aPixelsToTwips * mTopBorderWidth); + aBorder.bottom = NSToCoordRound(aPixelsToTwips * mBottomBorderWidth); + + return &aBorder; +} + #endif diff --git a/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp b/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp index f0ff97530bb..2405a50a342 100644 --- a/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp @@ -180,7 +180,7 @@ nsTableRowGroupFrame::InitRepeatedFrame(nsIPresContext* aPresContext, nsIAtom* frameType; copyCellFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableCellFrame == frameType) { + if (IS_TABLE_CELL(frameType)) { #ifdef NS_DEBUG nsIContent* content1; nsIContent* content2; @@ -360,6 +360,28 @@ nsTableRowGroupFrame::PlaceChild(nsIPresContext* aPresContext, } } +void +nsTableRowGroupFrame::InitChildReflowState(nsIPresContext& aPresContext, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsHTMLReflowState& aReflowState) +{ + nsMargin collapseBorder; + nsMargin padding(0,0,0,0); + nsMargin* pCollapseBorder = nsnull; + if (aBorderCollapse) { + if (aReflowState.frame) { + nsCOMPtr fType; + aReflowState.frame->GetFrameType(getter_AddRefs(fType)); + if (nsLayoutAtoms::tableRowFrame == fType.get()) { + nsTableRowFrame* rowFrame = (nsTableRowFrame*)aReflowState.frame; + pCollapseBorder = rowFrame->GetBCBorderWidth(aPixelsToTwips, collapseBorder); + } + } + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); +} + // Reflow the frames we've already created. If aDirtyOnly is set then only // reflow dirty frames. This assumes that all of the dirty frames are contiguous. NS_METHOD @@ -372,8 +394,10 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, nsTableRowFrame** aFirstRowReflowed) { nsTableFrame* tableFrame = nsnull; - nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (NS_FAILED(rv) || !tableFrame) return rv; + nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(rv); + + PRBool borderCollapse = tableFrame->IsBorderCollapse(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); nscoord cellSpacingY = tableFrame->GetCellSpacingY(); @@ -439,6 +463,7 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, } nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, kidFrame, kidAvailSize, reason); + InitChildReflowState(*aPresContext, borderCollapse, p2t, kidReflowState); // If this isn't the first row frame, then we can't be at the top of // the page anymore... @@ -555,7 +580,7 @@ HasMoreThanOneCell(nsTableCellMap* aCellMap, PRInt32 colIndex = 0; PRInt32 count = 0; do { - cellData = aCellMap->GetCellAt(aRowIndex, colIndex); + cellData = aCellMap->GetDataAt(aRowIndex, colIndex); if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan())) count++; if (count > 1) @@ -998,8 +1023,8 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, presShell->GetStyleSet(getter_AddRefs(styleSet)); if (!styleSet) {NS_ASSERTION(PR_FALSE, "no style set"); return NS_ERROR_NULL_POINTER;} - float p2t; - aPresContext->GetPixelsToTwips(&p2t); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + PRBool borderCollapse = ((nsTableFrame*)aTableFrame->GetFirstInFlow())->IsBorderCollapse(); nscoord availWidth = nsTableFrame::RoundToPixel(aReflowState.availableWidth, p2t); nscoord availHeight = nsTableFrame::RoundToPixel(aReflowState.availableHeight, p2t); @@ -1022,9 +1047,11 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, // row or there is at least 5% of the current page available if (!prevRowFrame || (availHeight - aDesiredSize.height > pageHeight / 20)) { // Reflow the row in the available space and have it split - nsSize availSize(availWidth, availHeight - bounds.y); - nsHTMLReflowState rowReflowState(aPresContext, aReflowState, rowFrame, - availSize, eReflowReason_Resize); + nsSize availSize(availWidth, availHeight - bounds.y); + nsHTMLReflowState rowReflowState(aPresContext, aReflowState, rowFrame, availSize, + eReflowReason_Resize); + InitChildReflowState(*aPresContext, borderCollapse, p2t, rowReflowState); + nsHTMLReflowMetrics desiredSize(nsnull); rv = ReflowChild(rowFrame, aPresContext, desiredSize, rowReflowState, @@ -1530,6 +1557,10 @@ nsTableRowGroupFrame::IR_TargetIsChild(nsIPresContext* aPresContext, { nsresult rv; + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(NS_ERROR_NULL_POINTER); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + // Recover the state as if aNextFrame is about to be reflowed RecoverState(aReflowState, aNextFrame); @@ -1540,9 +1571,11 @@ nsTableRowGroupFrame::IR_TargetIsChild(nsIPresContext* aPresContext, // Reflow the child giving it as much room as it wants. We'll deal with // splitting later after final determination of rows heights taking into // account cells with row spans... - nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE); - nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, - aNextFrame, kidAvailSize); + nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, aNextFrame, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, tableFrame->IsBorderCollapse(), p2t, kidReflowState); + nsSize kidMaxElementSize; nsHTMLReflowMetrics desiredSize(aDesiredSize.maxElementSize ? &kidMaxElementSize : nsnull, aDesiredSize.mFlags); @@ -1715,6 +1748,28 @@ nsTableRowGroupFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) cons } #endif +nsMargin* +nsTableRowGroupFrame::GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) +{ + aBorder.left = aBorder.right = 0; + + nsTableRowFrame* firstRowFrame = nsnull; + nsTableRowFrame* lastRowFrame = nsnull; + for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { + if(!firstRowFrame) { + firstRowFrame = rowFrame; + } + lastRowFrame = rowFrame; + } + if (firstRowFrame) { + aBorder.top = firstRowFrame->GetTopBCBorderWidth(&aPixelsToTwips); + aBorder.bottom = lastRowFrame->GetBottomBCBorderWidth(&aPixelsToTwips); + } + + return &aBorder; +} + //nsILineIterator methods for nsTableFrame NS_IMETHODIMP nsTableRowGroupFrame::GetNumLines(PRInt32* aResult) @@ -1757,7 +1812,7 @@ nsTableRowGroupFrame::GetLine(PRInt32 aLineNumber, *aLineFlags = 0;/// should we fill these in later? // not gonna touch aLineBounds right now - CellData* firstCellData = cellMap->GetCellAt(aLineNumber, 0); + CellData* firstCellData = cellMap->GetDataAt(aLineNumber, 0); if(!firstCellData) return NS_ERROR_FAILURE; @@ -1767,7 +1822,7 @@ nsTableRowGroupFrame::GetLine(PRInt32 aLineNumber, while((aLineNumber > 0)&&(!(*aFirstFrameOnLine))) { aLineNumber--; - firstCellData = cellMap->GetCellAt(aLineNumber, 0); + firstCellData = cellMap->GetDataAt(aLineNumber, 0); *aFirstFrameOnLine = (nsIFrame*)firstCellData->GetCellFrame(); } } @@ -1847,7 +1902,7 @@ nsTableRowGroupFrame::FindFrameAt(PRInt32 aLineNumber, PRBool gotParentRect = PR_FALSE; for(int i =0;i < cellCount; i++) { - cellData = cellMap->GetCellAt(aLineNumber, i); + cellData = cellMap->GetDataAt(aLineNumber, i); tempFrame = (nsIFrame*)cellData->GetCellFrame(); if(!tempFrame) @@ -1919,11 +1974,11 @@ nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32& colIndexRef = colIndex; cellFrame->GetColIndex(colIndexRef); - CellData* cellData = cellMap->GetCellAt(aLineNumber, colIndex + 1); + CellData* cellData = cellMap->GetDataAt(aLineNumber, colIndex + 1); if(!cellData)// if this isnt a valid cell, drop down and check the next line { - cellData = cellMap->GetCellAt(aLineNumber + 1, 0); + cellData = cellMap->GetDataAt(aLineNumber + 1, 0); if(!cellData) { //*aFrame = nsnull; @@ -1940,14 +1995,14 @@ nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, while((tempCol > 0) && (!aFrame)) { tempCol--; - cellData = cellMap->GetCellAt(aLineNumber, tempCol); + cellData = cellMap->GetDataAt(aLineNumber, tempCol); aFrame = (nsIFrame*)cellData->GetCellFrame(); if(!aFrame && (tempCol==0)) { while((tempRow > 0) && (!aFrame)) { tempRow--; - cellData = cellMap->GetCellAt(tempRow, 0); + cellData = cellMap->GetDataAt(tempRow, 0); aFrame = (nsIFrame*)cellData->GetCellFrame(); } } diff --git a/mozilla/layout/html/table/src/nsTableRowGroupFrame.h b/mozilla/layout/html/table/src/nsTableRowGroupFrame.h index 0a3859755b4..d259a63bdfb 100644 --- a/mozilla/layout/html/table/src/nsTableRowGroupFrame.h +++ b/mozilla/layout/html/table/src/nsTableRowGroupFrame.h @@ -216,6 +216,8 @@ public: nscoord GetHeightOfRows(nsIPresContext* aPresContext); nscoord GetHeightBasis(const nsHTMLReflowState& aReflowState); + nsMargin* GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder); // nsILineIterator methods public: NS_IMETHOD GetNumLines(PRInt32* aResult); @@ -251,6 +253,11 @@ public: protected: nsTableRowGroupFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsHTMLReflowState& aReflowState); + /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; diff --git a/mozilla/layout/style/html.css b/mozilla/layout/style/html.css index a501e25a540..61190f734f4 100644 --- a/mozilla/layout/style/html.css +++ b/mozilla/layout/style/html.css @@ -168,6 +168,10 @@ table[align="right"] { text-align: start; } +table[rules] { + border-collapse: collapse; +} + *|*:table-outer { display: table; margin: 0; @@ -219,6 +223,11 @@ tr { col, *|*:table-column { display: table-column; + border: inherit; + width: inherit; + height: inherit; + background: inherit; + border: inherit; } colgroup, *|*:table-column-group { diff --git a/mozilla/layout/style/nsCSSPropList.h b/mozilla/layout/style/nsCSSPropList.h index ce14a5df95e..3007f42b36f 100644 --- a/mozilla/layout/style/nsCSSPropList.h +++ b/mozilla/layout/style/nsCSSPropList.h @@ -103,7 +103,7 @@ CSS_PROP(border-bottom-color, border_bottom_color, VISUAL) CSS_PROP(-moz-border-bottom-colors, border_bottom_colors, VISUAL) CSS_PROP(border-bottom-style, border_bottom_style, REFLOW) // on/off will need reflow CSS_PROP(border-bottom-width, border_bottom_width, REFLOW) -CSS_PROP(border-collapse, border_collapse, REFLOW) +CSS_PROP(border-collapse, border_collapse, FRAMECHANGE) CSS_PROP(border-color, border_color, VISUAL) CSS_PROP(border-left, border_left, REFLOW) CSS_PROP(border-left-color, border_left_color, VISUAL) diff --git a/mozilla/layout/style/nsHTMLStyleSheet.cpp b/mozilla/layout/style/nsHTMLStyleSheet.cpp index 2421da50e03..1ec64eabe97 100644 --- a/mozilla/layout/style/nsHTMLStyleSheet.cpp +++ b/mozilla/layout/style/nsHTMLStyleSheet.cpp @@ -475,6 +475,254 @@ TableTHRule::MapRuleInfoInto(nsRuleData* aRuleData) return NS_OK; } +static void +ProcessTableRulesAttribute(nsStyleStruct* aStyleStruct, + nsRuleData* aRuleData, + PRUint8 aSide, + PRBool aGroup, + PRUint8 aRulesArg1, + PRUint8 aRulesArg2, + PRUint8 aRulesArg3) +{ + if (!aStyleStruct || !aRuleData || !aRuleData->mPresContext) return; + + nsCOMPtr tableContext = getter_AddRefs(aRuleData->mStyleContext->GetParent()); if (!tableContext) return; + if (!aGroup) { + tableContext = getter_AddRefs(tableContext->GetParent()); if (!tableContext) return; + } + + const nsStyleTable* tableData = + (const nsStyleTable*)tableContext->GetStyleData(eStyleStruct_Table); + if (tableData && ((aRulesArg1 == tableData->mRules) || + (aRulesArg2 == tableData->mRules) || + (aRulesArg3 == tableData->mRules))) { + const nsStyleBorder* tableBorderData = + (const nsStyleBorder*)tableContext->GetStyleData(eStyleStruct_Border); if (!tableBorderData) return; + PRUint8 tableBorderStyle = tableBorderData->GetBorderStyle(aSide); + + nsStyleBorder* borderData = (nsStyleBorder*)aStyleStruct; if (!borderData) return; + PRUint8 borderStyle = borderData->GetBorderStyle(aSide); + // XXX It appears that the style system erronously applies the custom style rule after css style, + // consequently it does not properly fit into the casade. For now, assume that a border style of none + // implies that the style has not been set. + if (NS_STYLE_BORDER_STYLE_NONE == borderStyle) { + // use the table's border style if it is dashed or dotted, otherwise use solid + PRUint8 bStyle = ((NS_STYLE_BORDER_STYLE_NONE != tableBorderStyle) && + (NS_STYLE_BORDER_STYLE_HIDDEN != tableBorderStyle)) + ? tableBorderStyle : NS_STYLE_BORDER_STYLE_SOLID; + if ((NS_STYLE_BORDER_STYLE_DASHED != bStyle) && + (NS_STYLE_BORDER_STYLE_DOTTED != bStyle) && + (NS_STYLE_BORDER_STYLE_SOLID != bStyle)) { + bStyle = NS_STYLE_BORDER_STYLE_SOLID; + } + bStyle |= NS_STYLE_BORDER_STYLE_RULES_MASK; + borderData->SetBorderStyle(aSide, bStyle); + + nscolor borderColor; + PRBool transparent, foreground; + borderData->GetBorderColor(aSide, borderColor, transparent, foreground); + if (transparent || foreground) { + // use the table's border color if it is set, otherwise use black + nscolor tableBorderColor; + tableBorderData->GetBorderColor(aSide, tableBorderColor, transparent, foreground); + borderColor = (transparent || foreground) ? NS_RGB(0,0,0) : tableBorderColor; + borderData->SetBorderColor(aSide, borderColor); + } + // set the border width to be 1 pixel + float p2t; + aRuleData->mPresContext->GetScaledPixelsToTwips(&p2t); + nscoord onePixel = NSToCoordRound(p2t); + nsStyleCoord coord(onePixel); + switch(aSide) { + case NS_SIDE_TOP: + borderData->mBorder.SetTop(coord); + break; + case NS_SIDE_RIGHT: + borderData->mBorder.SetRight(coord); + break; + case NS_SIDE_BOTTOM: + borderData->mBorder.SetBottom(coord); + break; + default: // NS_SIDE_LEFT + borderData->mBorder.SetLeft(coord); + break; + } + } + } +} + +// ----------------------------------------------------------- +// this rule handles borders on a , , when rules is set on its
+// ----------------------------------------------------------- +class TableTbodyRule: public GenericTableRule { +public: + TableTbodyRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableTbodyRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableTbodyRule::TableTbodyRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableTbodyRule::~TableTbodyRule() +{ +} + +static void TbodyPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_TOP, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_ROWS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_BOTTOM, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_ROWS); +} + +NS_IMETHODIMP +TableTbodyRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &TbodyPostResolveCallback; + } + return NS_OK; +} +// ----------------------------------------------------------- + +// ----------------------------------------------------------- +// this rule handles borders on a when rules is set on its
+// ----------------------------------------------------------- +class TableRowRule: public GenericTableRule { +public: + TableRowRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableRowRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableRowRule::TableRowRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableRowRule::~TableRowRule() +{ +} + +static void RowPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_TOP, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_ROWS, NS_STYLE_TABLE_RULES_ROWS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_BOTTOM, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_ROWS, NS_STYLE_TABLE_RULES_ROWS); +} + +NS_IMETHODIMP +TableRowRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &RowPostResolveCallback; + } + return NS_OK; +} + +// ----------------------------------------------------------- +// this rule handles borders on a when rules is set on its
+// ----------------------------------------------------------- +class TableColgroupRule: public GenericTableRule { +public: + TableColgroupRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableColgroupRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableColgroupRule::TableColgroupRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableColgroupRule::~TableColgroupRule() +{ +} + +static void ColgroupPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_LEFT, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_COLS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_RIGHT, PR_TRUE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_GROUPS, NS_STYLE_TABLE_RULES_COLS); +} + +NS_IMETHODIMP +TableColgroupRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &ColgroupPostResolveCallback; + } + return NS_OK; +} + +// ----------------------------------------------------------- +// this rule handles borders on a when rules is set on its
+// ----------------------------------------------------------- +class TableColRule: public GenericTableRule { +public: + TableColRule(nsIHTMLStyleSheet* aSheet); + virtual ~TableColRule(); + + void Reset() + { + GenericTableRule::Reset(); + } + + NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); +}; + +TableColRule::TableColRule(nsIHTMLStyleSheet* aSheet) +: GenericTableRule(aSheet) +{ +} + +TableColRule::~TableColRule() +{ +} + +static void ColPostResolveCallback(nsStyleStruct* aStyleStruct, nsRuleData* aRuleData) +{ + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_LEFT, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_COLS, NS_STYLE_TABLE_RULES_COLS); + ::ProcessTableRulesAttribute(aStyleStruct, aRuleData, NS_SIDE_RIGHT, PR_FALSE, NS_STYLE_TABLE_RULES_ALL, + NS_STYLE_TABLE_RULES_COLS, NS_STYLE_TABLE_RULES_COLS); +} + +NS_IMETHODIMP +TableColRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ + if (aRuleData && aRuleData->mSID == eStyleStruct_Border) { + aRuleData->mCanStoreInRuleTree = PR_FALSE; + aRuleData->mPostResolveCallback = &ColPostResolveCallback; + } + return NS_OK; +} // ----------------------------------------------------------- class AttributeKey: public nsHashKey @@ -653,6 +901,10 @@ protected: HTMLColorRule* mVisitedRule; HTMLColorRule* mActiveRule; HTMLDocumentColorRule* mDocumentColorRule; + TableTbodyRule* mTableTbodyRule; + TableRowRule* mTableRowRule; + TableColgroupRule* mTableColgroupRule; + TableColRule* mTableColRule; TableTHRule* mTableTHRule; // NOTE: if adding more rules, be sure to update // the SizeOf method to include them @@ -710,6 +962,26 @@ HTMLStyleSheetImpl::HTMLStyleSheetImpl(void) nsresult HTMLStyleSheetImpl::Init() { + mTableTbodyRule = new TableTbodyRule(this); + if (!mTableTbodyRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableTbodyRule); + + mTableRowRule = new TableRowRule(this); + if (!mTableRowRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableRowRule); + + mTableColgroupRule = new TableColgroupRule(this); + if (!mTableColgroupRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableColgroupRule); + + mTableColRule = new TableColRule(this); + if (!mTableColRule) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTableColRule); + mTableTHRule = new TableTHRule(this); if (!mTableTHRule) return NS_ERROR_OUT_OF_MEMORY; @@ -750,6 +1022,22 @@ HTMLStyleSheetImpl::~HTMLStyleSheetImpl() mDocumentColorRule->mSheet = nsnull; NS_RELEASE(mDocumentColorRule); } + if (nsnull != mTableTbodyRule) { + mTableTbodyRule->mSheet = nsnull; + NS_RELEASE(mTableTbodyRule); + } + if (nsnull != mTableRowRule) { + mTableRowRule->mSheet = nsnull; + NS_RELEASE(mTableRowRule); + } + if (nsnull != mTableColgroupRule) { + mTableColgroupRule->mSheet = nsnull; + NS_RELEASE(mTableColgroupRule); + } + if (nsnull != mTableColRule) { + mTableColRule->mSheet = nsnull; + NS_RELEASE(mTableColRule); + } if (nsnull != mTableTHRule) { mTableTHRule->mSheet = nsnull; NS_RELEASE(mTableTHRule); @@ -848,6 +1136,18 @@ HTMLStyleSheetImpl::RulesMatching(ElementRuleProcessorData* aData, else if (tag == nsHTMLAtoms::th) { ruleWalker->Forward(mTableTHRule); } + else if (tag == nsHTMLAtoms::tr) { + ruleWalker->Forward(mTableRowRule); + } + else if ((tag == nsHTMLAtoms::thead) || (tag == nsHTMLAtoms::tbody) || (tag == nsHTMLAtoms::tfoot)) { + ruleWalker->Forward(mTableTbodyRule); + } + else if (tag == nsHTMLAtoms::col) { + ruleWalker->Forward(mTableColRule); + } + else if (tag == nsHTMLAtoms::colgroup) { + ruleWalker->Forward(mTableColgroupRule); + } else if (tag == nsHTMLAtoms::table) { if (aData->mIsQuirkMode) ruleWalker->Forward(mDocumentColorRule); @@ -888,7 +1188,13 @@ NS_IMETHODIMP HTMLStyleSheetImpl::RulesMatching(PseudoRuleProcessorData* aData, nsIAtom* aMedium) { - // no pseudo frame style + nsIAtom* pseudoTag = aData->mPseudoTag; + if (pseudoTag == nsHTMLAtoms::tableColPseudo) { + nsRuleWalker *ruleWalker = aData->mRuleWalker; + if (ruleWalker) { + ruleWalker->Forward(mTableColRule); + } + } return NS_OK; } @@ -1008,6 +1314,11 @@ HTMLStyleSheetImpl::Reset(nsIURI* aURL) NS_RELEASE(mActiveRule); } mDocumentColorRule->Reset(); + + mTableTbodyRule->Reset(); + mTableRowRule->Reset(); + mTableColgroupRule->Reset(); + mTableColRule->Reset(); mTableTHRule->Reset(); mMappedAttrTable.Enumerate(MappedDropSheet); @@ -1264,6 +1575,10 @@ MappedSizeAttributes(nsHashKey *aKey, void *aData, void* closure) * - mVisitedRule * - mActiveRule * - mDocumentColorRule +* - mTableTbodyRule +* - mTableRowRule +* - mTableColgroupRule +* - mTableColRule * - mTableTHRule * - mMappedAttrTable * 2) Delegates (really) to the MappedAttributes in the mMappedAttrTable @@ -1298,6 +1613,10 @@ HTMLStyleSheetImpl::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) // - mVisitedRule : sizeof object // - mActiveRule : sizeof object // - mDocumentColorRule : sizeof object + // - mTableTbodyRule : sizeof object + // - mTableRowRule : sizeof object + // - mTableColgroupRule : sizeof object + // - mTableColRule : sizeof object // - mTableTHRule : sizeof object // - mMappedAttrTable @@ -1321,6 +1640,26 @@ HTMLStyleSheetImpl::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) tag = getter_AddRefs(NS_NewAtom("DocumentColorRule")); aSizeOfHandler->AddSize(tag,localSize); } + if(uniqueItems->AddItem((void*)mTableTbodyRule)){ + localSize = sizeof(*mTableTbodyRule); + tag = getter_AddRefs(NS_NewAtom("TableTbodyRule")); + aSizeOfHandler->AddSize(tag,localSize); + } + if(uniqueItems->AddItem((void*)mTableRowRule)){ + localSize = sizeof(*mTableRowRule); + tag = getter_AddRefs(NS_NewAtom("TableRowRule")); + aSizeOfHandler->AddSize(tag,localSize); + } + if(uniqueItems->AddItem((void*)mTableColgroupRule)){ + localSize = sizeof(*mTableColgroupRule); + tag = getter_AddRefs(NS_NewAtom("TableColgroupRule")); + aSizeOfHandler->AddSize(tag,localSize); + } + if(uniqueItems->AddItem((void*)mTableColRule)){ + localSize = sizeof(*mTableColRule); + tag = getter_AddRefs(NS_NewAtom("TableColRule")); + aSizeOfHandler->AddSize(tag,localSize); + } if(uniqueItems->AddItem((void*)mTableTHRule)){ localSize = sizeof(*mTableTHRule); tag = getter_AddRefs(NS_NewAtom("TableTHRule")); diff --git a/mozilla/layout/style/nsStyleStruct.cpp b/mozilla/layout/style/nsStyleStruct.cpp index 79bf6e4e9a9..4efb4816291 100644 --- a/mozilla/layout/style/nsStyleStruct.cpp +++ b/mozilla/layout/style/nsStyleStruct.cpp @@ -605,6 +605,28 @@ nsStyleBorder::CalcBorderFor(const nsIFrame* aFrame, nsMargin& aBorder) const } } +void +nsStyleBorder::CalcBorderFor(const nsIFrame* aFrame, PRUint8 aSide, nscoord& aWidth) const +{ + aWidth = 0; + // using mCachedBorder as above, doesn't work properly + nsStyleCoord coord; + switch(aSide) { + case NS_SIDE_TOP: + coord = mBorder.GetTop(coord); + break; + case NS_SIDE_RIGHT: + coord = mBorder.GetRight(coord); + break; + case NS_SIDE_BOTTOM: + coord = mBorder.GetBottom(coord); + break; + default: // NS_SIDE_LEFT + coord = mBorder.GetLeft(coord); + } + aWidth = CalcSideFor(aFrame, coord, NS_SPACING_BORDER, aSide, mBorderWidths, 3); +} + nsStyleOutline::nsStyleOutline(nsIPresContext* aPresContext) { // XXX support mBorderWidths until deprecated methods are removed @@ -862,7 +884,7 @@ nsStyleTable::nsStyleTable() mLayoutStrategy = NS_STYLE_TABLE_LAYOUT_AUTO; mCols = NS_STYLE_TABLE_COLS_NONE; mFrame = NS_STYLE_TABLE_FRAME_NONE; - mRules = NS_STYLE_TABLE_RULES_ALL; + mRules = NS_STYLE_TABLE_RULES_NONE; mSpan = 1; } diff --git a/mozilla/layout/style/nsStyleStruct.h b/mozilla/layout/style/nsStyleStruct.h index 9627f3804b8..2303efea81c 100644 --- a/mozilla/layout/style/nsStyleStruct.h +++ b/mozilla/layout/style/nsStyleStruct.h @@ -465,7 +465,8 @@ struct nsStyleBorder: public nsStyleStruct { // XXX these are deprecated methods void CalcBorderFor(const nsIFrame* aFrame, nsMargin& aBorder) const; - + void CalcBorderFor(const nsIFrame* aFrame, PRUint8 aSide, nscoord& aWidth) const; + protected: PRPackedBool mHasCachedBorder; nsMargin mCachedBorder; diff --git a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp index 479cc30158f..b79d4379a67 100644 --- a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp +++ b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp @@ -139,7 +139,7 @@ PRBool BasicTableLayoutStrategy::Initialize(nsIPresContext* aPresContex nscoord minWidth, prefWidth; mTableFrame->CalcMinAndPreferredWidths(aPresContext, aReflowState, PR_FALSE, minWidth, prefWidth); if (hasPctCol && mTableFrame->IsAutoWidth()) { - prefWidth = CalcPctAdjTableWidth(aReflowState, boxWidth, p2t); + prefWidth = CalcPctAdjTableWidth(*aPresContext, aReflowState, boxWidth, p2t); } // calc the desired width, considering if there is a specified width. // don't use nsTableFrame::CalcDesiredWidth because it is based on table column widths. @@ -195,7 +195,7 @@ ResetPctValues(nsTableFrame* aTableFrame, // initialize the col percent and cell percent values to 0. PRInt32 colX; for (colX = 0; colX < aNumCols; colX++) { - nsTableColFrame* colFrame = aTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = aTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (colFrame) { colFrame->SetWidth(PCT, WIDTH_NOT_SET); colFrame->SetWidth(PCT_ADJ, WIDTH_NOT_SET); @@ -207,6 +207,7 @@ PRBool BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState) { + if (!aPresContext) ABORT1(PR_FALSE); #ifdef DEBUG_TABLE_STRATEGY printf("BalanceColumnWidths en count=%d \n", gsDebugCount++); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE); #endif @@ -220,8 +221,16 @@ BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresCont PRInt32 numCols = mTableFrame->GetColCount(); PRBool tableIsAutoWidth = mTableFrame->IsAutoWidth(); - nscoord horBorderPadding = aReflowState.mComputedBorderPadding.left + - aReflowState.mComputedBorderPadding.right; + + nscoord horOffset; + // get the reduction in available horizontal space due to borders and padding + if (mTableFrame->IsBorderCollapse()) { + nsMargin offset = mTableFrame->GetChildAreaOffset(*aPresContext, &aReflowState); + horOffset = offset.left + offset.right; + } + else { + horOffset = aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; + } // determine if the table is auto/fixed and get the fixed width if available nscoord maxWidth = mTableFrame->CalcBorderBoxWidth(aPresContext, aReflowState); @@ -238,19 +247,19 @@ BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresCont // An auto table returns a new table width based on percent cells/cols if they exist nscoord perAdjTableWidth = 0; if (mTableFrame->HasPctCol()) { - perAdjTableWidth = AssignPctColumnWidths(aReflowState, maxWidth, tableIsAutoWidth, p2t); + perAdjTableWidth = AssignPctColumnWidths(*aPresContext, aReflowState, maxWidth, tableIsAutoWidth, p2t); if (perAdjTableWidth > 0) { // if an auto table has a pct col or cell, set the preferred table width // here so that CalcPctAdjTableWidth wont't need to be called by the table mTableFrame->SetPreferredWidth(perAdjTableWidth); } perAdjTableWidth = PR_MIN(perAdjTableWidth, maxWidth); - perAdjTableWidth -= horBorderPadding; + perAdjTableWidth -= horOffset; perAdjTableWidth = PR_MAX(perAdjTableWidth, 0); } // reduce the maxWidth by border and padding, since we will be dealing with content width - maxWidth -= horBorderPadding; + maxWidth -= horOffset; maxWidth = PR_MAX(0, maxWidth); PRInt32 numNonZeroWidthCols = 0; @@ -258,7 +267,7 @@ BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresCont nscoord minTableWidth = 0; PRInt32 colX; for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord colMinWidth = colFrame->GetMinWidth(); mTableFrame->SetColumnWidth(colX, colMinWidth); @@ -400,7 +409,7 @@ void BasicTableLayoutStrategy::AllocateFully(nscoord& aTotalAllocated, { PRInt32 numCols = mTableFrame->GetColCount(); for (PRInt32 colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; @@ -454,7 +463,7 @@ void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, } else { if (aExclude0Pro) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (colFrame->GetConstraint() == e0ProportionConstraint) { aAllocTypes[colX] = FINISHED; } @@ -467,7 +476,7 @@ void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, PRInt32 numColsAllocated = 0; PRInt32 totalAllocated = 0; for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); PRBool skipColumn = aExclude0Pro && (e0ProportionConstraint == colFrame->GetConstraint()); if (FINISHED != aAllocTypes[colX] && !skipColumn ) { divisor += mTableFrame->GetColumnWidth(colX); @@ -477,7 +486,7 @@ void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, for (colX = 0; colX < numCols; colX++) { if (FINISHED != aAllocTypes[colX]) { if (aExclude0Pro) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (colFrame && (e0ProportionConstraint == colFrame->GetConstraint())) { continue; } @@ -533,7 +542,7 @@ BasicTableLayoutStrategy::ComputeNonPctColspanWidths(const nsHTMLReflowState& aR PRInt32 colX; for (colX = numCols - 1; colX >= 0; colX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; colFrame->SetWidth(MIN_ADJ, WIDTH_NOT_SET); colFrame->SetWidth(FIX_ADJ, WIDTH_NOT_SET); @@ -698,7 +707,7 @@ BasicTableLayoutStrategy::ComputeNonPctColspanWidths(PRInt32 aWidthInd PRInt32 spanX; // accumulate the various divisors to be used later for (spanX = 0; spanX < aColSpan; spanX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord minWidth = PR_MAX(colFrame->GetMinWidth(), 0); nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0); @@ -802,7 +811,7 @@ BasicTableLayoutStrategy::ComputeNonPctColspanWidths(PRInt32 aWidthInd // get the correct numerator in a similar fashion to getting the divisor for (spanX = 0; spanX < aColSpan; spanX++) { if (usedWidth >= availWidth) break; - nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord minWidth = colFrame->GetMinWidth(); nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0); @@ -974,7 +983,7 @@ BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPre nscoord fixWidth = WIDTH_NOT_SET; // Get column frame and reset it - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; NS_ASSERTION(nsnull != colFrame, "bad col frame"); colFrame->ResetSizingInfo(); @@ -1079,7 +1088,7 @@ BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPre } //set the table col fixed width if present for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); if (!colFrame) continue; nscoord fixColWidth = colFrame->GetWidth(FIX); // use the style width of a col only if the col hasn't gotten a fixed width from any cell @@ -1140,7 +1149,7 @@ BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPre // Set the table col width for each col to the content min. for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(PR_FALSE); nscoord minWidth = colFrame->GetMinWidth(); mTableFrame->SetColumnWidth(colX, minWidth); } @@ -1159,7 +1168,7 @@ BasicTableLayoutStrategy::ReduceOverSpecifiedPctCols(nscoord aExcess) { nscoord numCols = mTableFrame->GetColCount(); for (PRInt32 colX = numCols - 1; (colX >= 0) && (aExcess > 0); colX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; nscoord pctWidth = colFrame->GetWidth(PCT); nscoord reduction = 0; @@ -1198,7 +1207,8 @@ inline nscoord WrapupAssignPctColumnWidths(nsTableFrame* aTableFrame, #endif nscoord -BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState, +BasicTableLayoutStrategy::CalcPctAdjTableWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidthIn, float aPixelToTwips) { @@ -1219,7 +1229,7 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS rawPctValues[colX] = 0.0f; } - nsMargin borderPadding = mTableFrame->GetBorderPadding(aReflowState); + nsMargin borderPadding = mTableFrame->GetContentAreaOffset(aPresContext, &aReflowState); nscoord availWidth = aAvailWidthIn; if (NS_UNCONSTRAINEDSIZE != availWidth) { // adjust the avail width to exclude table border, padding and cell spacing @@ -1227,7 +1237,7 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS } for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(0); if (!colFrame) continue; nscoord maxColBasis = -1; // Scan the cells in the col @@ -1282,7 +1292,7 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS nscoord fixDesTotalNoPct = 0; // total of fix or des widths of cols without pct for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(0); nscoord fixWidth = colFrame->GetFixWidth(); nscoord fixDesWidth = (fixWidth > 0) ? fixWidth : colFrame->GetDesWidth(); fixDesTotal += fixDesWidth; @@ -1329,7 +1339,8 @@ BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowS // Determine percentage col widths for each col frame nscoord -BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflowState, +BasicTableLayoutStrategy::AssignPctColumnWidths(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidth, PRBool aTableIsAutoWidth, float aPixelToTwips) @@ -1349,11 +1360,11 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // on percent cells/cols. This probably should only be a NavQuirks thing, since // a percentage based cell or column on an auto table should force the column to auto nscoord basis = (aTableIsAutoWidth) - ? CalcPctAdjTableWidth(aReflowState, aAvailWidth, aPixelToTwips) + ? CalcPctAdjTableWidth(aPresContext, aReflowState, aAvailWidth, aPixelToTwips) : aAvailWidth; // adjust the basis to exclude table border, padding and cell spacing - nsMargin borderPadding = mTableFrame->GetBorderPadding(aReflowState); + nsMargin borderPadding = mTableFrame->GetContentAreaOffset(aPresContext, &aReflowState); basis -= borderPadding.left + borderPadding.right + mCellSpacingTotal; nscoord colPctTotal = 0; @@ -1361,7 +1372,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // Determine the percentage contribution for cols and for cells with colspan = 1 // Iterate backwards, similarly to the reasoning in AssignNonPctColumnWidths for (colX = numCols - 1; colX >= 0; colX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT1(0); if (!colFrame) continue; nscoord maxColPctWidth = WIDTH_NOT_SET; float maxColPct = 0.0f; @@ -1431,7 +1442,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow } // determine if the cell spans cols which have a pct value for (PRInt32 spanX = 0; (spanX < colSpan) && !done; spanX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) ABORT1(0); if (!colFrame) continue; if (colFrame->GetWidth(PCT) > 0) { mTableFrame->SetHasCellSpanningPctCol(PR_TRUE); @@ -1511,7 +1522,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // accumulate the spanTotal as the max of MIN, DES, FIX, PCT PRInt32 spanX; for (spanX = 0; spanX < colSpan; spanX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) ABORT1(0); if (!colFrame) continue; nscoord colPctWidth = colFrame->GetWidth(PCT); if (colPctWidth > 0) { // skip pct cols @@ -1546,7 +1557,7 @@ BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflow // record the percent contributions for the spanned cols PRInt32 usedColumns = colSpan; for (spanX = colSpan-1; spanX >= 0; spanX--) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) ABORT1(0); if (!colFrame) continue; if ((colFrame->GetWidth(PCT) > 0) || (canSkipPctAdj && (colFrame->GetWidth(PCT_ADJ) > 0))) { // dont use pct cols or if we can skip the pct adj event do not take the PCT_ADJ cols @@ -1621,7 +1632,7 @@ void BasicTableLayoutStrategy::CalculateTotals(PRInt32* aTotalCounts, PRInt32 colX; for (colX = 0; colX < numEffCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; nscoord minCol = colFrame->GetMinWidth(); aTotalCounts[MIN_CON]++; @@ -1820,7 +1831,7 @@ void BasicTableLayoutStrategy::AllocateConstrained(PRInt32 aAvailWidth, PRInt32 colX; // find out how many constrained cols there are for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; @@ -1837,7 +1848,7 @@ void BasicTableLayoutStrategy::AllocateConstrained(PRInt32 aAvailWidth, PRInt32 constrColX = 0; // set the col info entries for each constrained col for (colX = 0; colX < numCols; colX++) { - nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); + nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) ABORT0(); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; diff --git a/mozilla/layout/tables/BasicTableLayoutStrategy.h b/mozilla/layout/tables/BasicTableLayoutStrategy.h index 793b8b79d40..69883aabd2c 100644 --- a/mozilla/layout/tables/BasicTableLayoutStrategy.h +++ b/mozilla/layout/tables/BasicTableLayoutStrategy.h @@ -93,7 +93,8 @@ public: * @param aPixelToTwips - the number of twips in a pixel. * @return - the basis for percent calculations */ - virtual nscoord CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState, + virtual nscoord CalcPctAdjTableWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidth, float aPixelToTwips); void Dump(PRInt32 aIndent); @@ -159,7 +160,8 @@ protected: * @param aPixelToTwips - the number of twips in a pixel. * @return - the adjusted basis including table border, padding and cell spacing */ - nscoord AssignPctColumnWidths(const nsHTMLReflowState& aReflowState, + nscoord AssignPctColumnWidths(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aBasis, PRBool aTableIsAutoWidth, float aPixelToTwips); diff --git a/mozilla/layout/tables/celldata.h b/mozilla/layout/tables/celldata.h index cf977be70d6..b281df0e2e2 100644 --- a/mozilla/layout/tables/celldata.h +++ b/mozilla/layout/tables/celldata.h @@ -38,6 +38,7 @@ #define CellData_h__ #include "nsISupports.h" +#include "nsCoord.h" class nsTableCellFrame; @@ -51,8 +52,9 @@ public: ~CellData(); + void Init(nsTableCellFrame* aCellFrame); PRBool IsOrig() const; - + PRBool IsDead() const; PRBool IsSpan() const; PRBool IsRowSpan() const; @@ -84,6 +86,93 @@ protected: }; }; +// Border Collapsing Cell Data +enum BCBorderOwner +{ + eTableOwner = 0, + eColGroupOwner = 1, + eAjaColGroupOwner = 2, // col group to the left + eColOwner = 3, + eAjaColOwner = 4, // col to the left + eRowGroupOwner = 5, + eAjaRowGroupOwner = 6, // row group above + eRowOwner = 7, + eAjaRowOwner = 8, // row above + eCellOwner = 9, + eAjaCellOwner = 10, // cell to the top or to the left +}; + +// These are the max sizes that are stored. If they are exceeded, then the max is stored and +// the actual value is computed when needed. +#define MAX_BORDER_WIDTH 64 +#define MAX_CORNER_SUB_WIDTH 128 + +// BCData stores the top and left border info and the corner connecting the two. +class BCData +{ +public: + BCData(); + + ~BCData(); + + nscoord GetLeftEdge(BCBorderOwner& aOwner, + PRBool& aStart) const; + + void SetLeftEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart); + + nscoord GetTopEdge(BCBorderOwner& aOwner, + PRBool& aStart) const; + + void SetTopEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart); + + PRUint8 GetCorner(PRUint8& aCornerOwner, + PRPackedBool& aBevel) const; + + void SetCorner(PRUint8 aOwner, + PRUint8 aSubSize, + PRBool aBevel); + + PRBool IsLeftStart() const; + + void SetLeftStart(PRBool aValue); + + PRBool IsTopStart() const; + + void SetTopStart(PRBool aValue); + + +protected: + unsigned mLeftOwner: 4; // owner of left border + unsigned mLeftSize: 6; // size in pixels of left border + unsigned mLeftStart: 1; // set if this is the start of a vertical border segment + unsigned mCornerSide: 2; // side of the owner of the upper left corner relative to the corner + unsigned mCornerSubSize: 7; // size of the largest border not in the dominate plane (for example, if + // corner is owned by the segment to its top or bottom, then the size is the + // max of the border sizes of the segments to its left or right. + unsigned mCornerBevel: 1; // is the corner beveled (only two segments, perpendicular, not dashed or dotted). + unsigned mTopOwner: 4; // owner of top border + unsigned mTopSize: 6; // size in pixels of top border + unsigned mTopStart: 1; // set if this is the start of a horizontal border segment +}; + +// BCCellData entries replace CellData entries in the cell map if the border collapsing model is in +// effect. BCData for a row and col entry contains the left and top borders of cell at that row and +// col and the corner connecting the two. The right borders of the cells in the last col and the bottom +// borders of the last row are stored in separate BCData entries in the cell map. +class BCCellData : public CellData +{ +public: + BCCellData(nsTableCellFrame* aOrigCell); + ~BCCellData(); + + BCData mData; +}; + + #define SPAN 0x00000001 // there a row or col span #define ROW_SPAN 0x00000002 // there is a row span #define ROW_SPAN_0 0x00000004 // the row span is 0 @@ -103,9 +192,19 @@ inline nsTableCellFrame* CellData::GetCellFrame() const return nsnull; } +inline void CellData::Init(nsTableCellFrame* aCellFrame) +{ + mOrigCell = aCellFrame; +} + inline PRBool CellData::IsOrig() const { - return (SPAN != (SPAN & mBits)); + return ((nsnull != mOrigCell) && (SPAN != (SPAN & mBits))); +} + +inline PRBool CellData::IsDead() const +{ + return (0 == mBits); } inline PRBool CellData::IsSpan() const @@ -213,4 +312,88 @@ inline void CellData::SetOverlap(PRBool aOverlap) } } +inline BCData::BCData() +{ + mLeftOwner = mTopOwner = eCellOwner; + mLeftStart = mTopStart = 1; + mLeftSize = mCornerSide = mCornerSubSize = mTopSize = 0; +} + +inline BCData::~BCData() +{ +} + +inline nscoord BCData::GetLeftEdge(BCBorderOwner& aOwner, + PRBool& aStart) const +{ + aOwner = (BCBorderOwner)mLeftOwner; + aStart = (PRBool)mLeftStart; + + return (nscoord)mLeftSize; +} + +inline void BCData::SetLeftEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart) +{ + mLeftOwner = aOwner; + mLeftSize = (aSize > MAX_BORDER_WIDTH) ? MAX_BORDER_WIDTH : aSize; + mLeftStart = aStart; +} + +inline nscoord BCData::GetTopEdge(BCBorderOwner& aOwner, + PRBool& aStart) const +{ + aOwner = (BCBorderOwner)mTopOwner; + aStart = (PRBool)mTopStart; + + return (nscoord)mTopSize; +} + +inline void BCData::SetTopEdge(BCBorderOwner aOwner, + nscoord aSize, + PRBool aStart) +{ + mTopOwner = aOwner; + mTopSize = (aSize > MAX_BORDER_WIDTH) ? MAX_BORDER_WIDTH : aSize; + mTopStart = aStart; +} + +inline PRUint8 BCData::GetCorner(PRUint8& aOwnerSide, + PRPackedBool& aBevel) const +{ + aOwnerSide = mCornerSide; + aBevel = (PRBool)mCornerBevel; + return (PRUint8)mCornerSubSize; +} + +inline void BCData::SetCorner(PRUint8 aSubSize, + PRUint8 aOwnerSide, + PRBool aBevel) +{ + mCornerSubSize = (aSubSize > MAX_CORNER_SUB_WIDTH) ? MAX_CORNER_SUB_WIDTH : aSubSize; + mCornerSide = aOwnerSide; + mCornerBevel = aBevel; +} + +inline PRBool BCData::IsLeftStart() const +{ + return (PRBool)mLeftStart; +} + +inline void BCData::SetLeftStart(PRBool aValue) +{ + mLeftStart = aValue; +} + +inline PRBool BCData::IsTopStart() const +{ + return (PRBool)mTopStart; +} + +inline void BCData::SetTopStart(PRBool aValue) +{ + mTopStart = aValue; +} + #endif diff --git a/mozilla/layout/tables/nsCellMap.cpp b/mozilla/layout/tables/nsCellMap.cpp index 167268997e0..3b6a835cfe8 100644 --- a/mozilla/layout/tables/nsCellMap.cpp +++ b/mozilla/layout/tables/nsCellMap.cpp @@ -60,12 +60,25 @@ CellData::~CellData() MOZ_COUNT_DTOR(CellData); } +BCCellData::BCCellData(nsTableCellFrame* aOrigCell) +:CellData(aOrigCell) +{ + MOZ_COUNT_CTOR(BCCellData); +} + +BCCellData::~BCCellData() +{ + MOZ_COUNT_DTOR(BCCellData); +} + MOZ_DECL_CTOR_COUNTER(nsCellMap) // nsTableCellMap -nsTableCellMap::nsTableCellMap(nsIPresContext* aPresContext, nsTableFrame& aTableFrame) -:mTableFrame(aTableFrame), mFirstMap(nsnull) +nsTableCellMap::nsTableCellMap(nsIPresContext* aPresContext, + nsTableFrame& aTableFrame, + PRBool aBorderCollapse) +:mTableFrame(aTableFrame), mFirstMap(nsnull), mBCInfo(nsnull) { MOZ_COUNT_CTOR(nsTableCellMap); @@ -83,11 +96,17 @@ nsTableCellMap::nsTableCellMap(nsIPresContext* aPresContext, nsTableFrame& aTabl InsertGroupCellMap(*rgFrame, prior); } } + if (aBorderCollapse) { + mBCInfo = new BCInfo(); + } } nsTableCellMap::~nsTableCellMap() { MOZ_COUNT_DTOR(nsTableCellMap); + + PRInt32 rowCount = GetRowCount(); + nsCellMap* cellMap = mFirstMap; while (cellMap) { nsCellMap* next = cellMap->GetNextSibling(); @@ -95,15 +114,89 @@ nsTableCellMap::~nsTableCellMap() cellMap = next; } - PRInt32 colCount = mCols.Count(); + PRInt32 colCount = mCols.Count(); for (PRInt32 colX = 0; colX < colCount; colX++) { nsColInfo* colInfo = (nsColInfo *)mCols.ElementAt(colX); if (colInfo) { delete colInfo; } } + if (mBCInfo) { + DeleteRightBottomBorders(); + delete mBCInfo; + } } +// Get the bcData holding the border segments of the right edge of the table +BCData* +nsTableCellMap::GetRightMostBorder(PRInt32 aRowIndex) +{ + if (!mBCInfo) ABORT1(nsnull); + + BCData* bcData; + + PRInt32 numRows = mBCInfo->mRightBorders.Count(); + if (aRowIndex < numRows) { + bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(aRowIndex); + } + else { + for (PRInt32 rowX = numRows; rowX <= aRowIndex; rowX++) { + bcData = new BCData(); if (!bcData) ABORT1(nsnull); + mBCInfo->mRightBorders.AppendElement(bcData); + } + } + return bcData; +} + +// Get the bcData holding the border segments of the bottom edge of the table +BCData* +nsTableCellMap::GetBottomMostBorder(PRInt32 aColIndex) +{ + if (!mBCInfo) ABORT1(nsnull); + + BCData* bcData; + + PRInt32 numCols = mBCInfo->mBottomBorders.Count(); + if (aColIndex < numCols) { + bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(aColIndex); + } + else { + for (PRInt32 colX = numCols; colX <= aColIndex; colX++) { + bcData = new BCData(); if (!bcData) ABORT1(nsnull); + mBCInfo->mBottomBorders.AppendElement(bcData); + } + } + return bcData; +} + +// delete the borders corresponding to the right and bottom edges of the table +void +nsTableCellMap::DeleteRightBottomBorders() +{ + if (mBCInfo) { + PRInt32 numCols = mBCInfo->mBottomBorders.Count(); + if (numCols > 0) { + for (PRInt32 colX = numCols - 1; colX >= 0; colX--) { + BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX); + if (bcData) { + delete bcData; + } + mBCInfo->mBottomBorders.RemoveElementAt(colX); + } + } + PRUint32 numRows = mBCInfo->mRightBorders.Count(); + if (numRows > 0) { + for (PRInt32 rowX = numRows - 1; rowX >= 0; rowX--) { + BCData* bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(rowX); + if (bcData) { + delete bcData; + } + mBCInfo->mRightBorders.RemoveElementAt(rowX); + } + } + } +} + void nsTableCellMap::InsertGroupCellMap(nsCellMap* aPrevMap, nsCellMap& aNewMap) @@ -126,9 +219,11 @@ void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame& aNewGroup, nsCellMap* newMap = new nsCellMap(aNewGroup); if (newMap) { nsCellMap* prevMap = nsnull; + nsCellMap* lastMap = mFirstMap; if (aPrevGroup) { nsCellMap* map = mFirstMap; while (map) { + lastMap = map; if (map->GetRowGroup() == aPrevGroup) { prevMap = map; break; @@ -137,7 +232,13 @@ void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame& aNewGroup, } } if (!prevMap) { - aPrevGroup = nsnull; + if (aPrevGroup) { + prevMap = lastMap; + aPrevGroup = (prevMap) ? prevMap->GetRowGroup() : nsnull; + } + else { + aPrevGroup = nsnull; + } } InsertGroupCellMap(prevMap, *newMap); } @@ -164,6 +265,16 @@ void nsTableCellMap::RemoveGroupCellMap(nsTableRowGroupFrame* aGroup) } } +nsCellMap* +nsTableCellMap::GetMapFor(nsTableRowGroupFrame& aRowGroup) +{ + for (nsCellMap* map = mFirstMap; map; map = map->GetNextSibling()) { + if (&aRowGroup == map->GetRowGroup()) { + return map; + } + } + return nsnull; +} PRInt32 @@ -241,14 +352,15 @@ nsTableCellMap::GetRowCount() const } CellData* -nsTableCellMap::GetCellAt(PRInt32 aRowIndex, - PRInt32 aColIndex) +nsTableCellMap::GetDataAt(PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan) { PRInt32 rowIndex = aRowIndex; nsCellMap* map = mFirstMap; while (map) { if (map->GetRowCount() > rowIndex) { - return map->GetCellAt(*this, rowIndex, aColIndex); + return map->GetDataAt(*this, rowIndex, aColIndex, aUpdateZeroSpan); } rowIndex -= map->GetRowCount(); map = map->GetNextSibling(); @@ -268,6 +380,12 @@ nsTableCellMap::AddColsAtEnd(PRUint32 aNumCols) if (colInfo) { mCols.AppendElement(colInfo); } + if (mBCInfo) { + BCData* bcData = new BCData(); + if (bcData) { + mBCInfo->mBottomBorders.AppendElement(bcData); + } + } } } @@ -283,6 +401,16 @@ nsTableCellMap::RemoveColsAtEnd() if (colInfo) { if ((colInfo->mNumCellsOrig <= 0) && (colInfo->mNumCellsSpan <= 0)) { mCols.RemoveElementAt(colX); + if (mBCInfo) { + PRInt32 count = mBCInfo->mBottomBorders.Count(); + if (colX < count) { + BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX); + if (bcData) { + delete bcData; + } + mBCInfo->mBottomBorders.RemoveElementAt(colX); + } + } } else break; // only remove until we encounter the 1st valid one } @@ -298,21 +426,44 @@ nsTableCellMap::InsertRows(nsIPresContext* aPresContext, nsTableRowGroupFrame& aParent, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { - NS_ASSERTION((aRows.Count() > 0) && (aFirstRowIndex >= 0), "nsTableCellMap::InsertRows called incorrectly"); + PRInt32 numNewRows = aRows.Count(); + if ((numNewRows <= 0) || (aFirstRowIndex < 0)) ABORT0(); + PRInt32 rowIndex = aFirstRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { - if (cellMap->GetRowGroup() == &aParent) { - cellMap->InsertRows(aPresContext, *this, aRows, rowIndex, aConsiderSpans); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + if (rg == &aParent) { + cellMap->InsertRows(aPresContext, *this, aRows, rowIndex, aConsiderSpans, aDamageArea); + aDamageArea.y = aFirstRowIndex; + aDamageArea.height = PR_MAX(0, GetRowCount() - aFirstRowIndex); //Dump("after InsertRows"); + if (mBCInfo) { + BCData* bcData; + PRInt32 count = mBCInfo->mRightBorders.Count(); + if (aFirstRowIndex < count) { + for (PRInt32 rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) { + bcData = new BCData(); if (!bcData) ABORT0(); + mBCInfo->mRightBorders.InsertElementAt(bcData, rowX); + } + } + else { + GetRightMostBorder(aFirstRowIndex); // this will create missing entries + for (PRInt32 rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) { + bcData = new BCData(); if (!bcData) ABORT0(); + mBCInfo->mRightBorders.AppendElement(bcData); + } + } + } return; } rowIndex -= cellMap->GetRowCount(); cellMap = cellMap->GetNextSibling(); } - + NS_ASSERTION(PR_FALSE, "Attempt to insert row into wrong map."); } @@ -320,13 +471,29 @@ void nsTableCellMap::RemoveRows(nsIPresContext* aPresContext, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { PRInt32 rowIndex = aFirstRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowCount() > rowIndex) { - cellMap->RemoveRows(aPresContext, *this, rowIndex, aNumRowsToRemove, aConsiderSpans); + cellMap->RemoveRows(aPresContext, *this, rowIndex, aNumRowsToRemove, aConsiderSpans, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; + aDamageArea.height = PR_MAX(0, GetRowCount() - aFirstRowIndex); + if (mBCInfo) { + BCData* bcData; + for (PRInt32 rowX = aFirstRowIndex + aNumRowsToRemove - 1; rowX >= aFirstRowIndex; rowX--) { + if (rowX < mBCInfo->mRightBorders.Count()) { + bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(rowX); + if (bcData) { + delete bcData; + } + mBCInfo->mRightBorders.RemoveElementAt(rowX); + } + } + } break; } rowIndex -= cellMap->GetRowCount(); @@ -344,7 +511,7 @@ nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) PRInt32 colIndex = 0; do { - cellData = GetCellAt(aRowIndex, colIndex); + cellData = GetDataAt(aRowIndex, colIndex); if (cellData && cellData->GetCellFrame()) originCount++; colIndex++; @@ -353,10 +520,11 @@ nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) return originCount; } -PRInt32 +CellData* nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, PRInt32 aRowIndex, - PRBool aRebuildIfNecessary) + PRBool aRebuildIfNecessary, + nsRect& aDamageArea) { NS_ASSERTION(&aCellFrame == aCellFrame.GetFirstInFlow(), "invalid call on continuing frame"); nsIFrame* rgFrame = nsnull; @@ -365,12 +533,14 @@ nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, rgFrame->GetParent(&rgFrame); // get the row group if (!rgFrame) return 0; - PRInt32 result = 0; + CellData* result = nsnull; PRInt32 rowIndex = aRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowGroup() == rgFrame) { - result = cellMap->AppendCell(*this, aCellFrame, rowIndex, aRebuildIfNecessary); + result = cellMap->AppendCell(*this, &aCellFrame, rowIndex, aRebuildIfNecessary, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; break; } rowIndex -= cellMap->GetRowCount(); @@ -384,13 +554,17 @@ nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, void nsTableCellMap::InsertCells(nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore) + PRInt32 aColIndexBefore, + nsRect& aDamageArea) { PRInt32 rowIndex = aRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowCount() > rowIndex) { - cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore); + cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; + aDamageArea.width = PR_MAX(0, GetColCount() - aColIndexBefore); break; } rowIndex -= cellMap->GetRowCount(); @@ -402,15 +576,22 @@ nsTableCellMap::InsertCells(nsVoidArray& aCellFrames, void nsTableCellMap::RemoveCell(nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex) + PRInt32 aRowIndex, + nsRect& aDamageArea) { + if (!aCellFrame) ABORT0(); NS_ASSERTION(aCellFrame == (nsTableCellFrame *)aCellFrame->GetFirstInFlow(), "invalid call on continuing frame"); PRInt32 rowIndex = aRowIndex; nsCellMap* cellMap = mFirstMap; while (cellMap) { if (cellMap->GetRowCount() > rowIndex) { - cellMap->RemoveCell(*this, aCellFrame, rowIndex); + cellMap->RemoveCell(*this, aCellFrame, rowIndex, aDamageArea); + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); + aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0; + PRInt32 colIndex; + aCellFrame->GetColIndex(colIndex); + aDamageArea.width = PR_MAX(0, GetColCount() - colIndex); break; } rowIndex -= cellMap->GetRowCount(); @@ -448,7 +629,7 @@ nsTableCellMap::Dump(char* aString) const } nsCellMap* cellMap = mFirstMap; while (cellMap) { - cellMap->Dump(); + cellMap->Dump(nsnull != mBCInfo); cellMap = cellMap->GetNextSibling(); } printf("***** END TABLE CELL MAP DUMP *****\n"); @@ -525,6 +706,237 @@ PRBool nsTableCellMap::ColHasSpanningCells(PRInt32 aColIndex) return PR_FALSE; } +BCData* +nsTableCellMap::GetBCData(PRUint8 aSide, + nsCellMap& aCellMap, + PRUint32 aRowIndex, + PRUint32 aColIndex, + PRBool aIsLowerRight) +{ + if (!mBCInfo || aIsLowerRight) ABORT1(nsnull); + + BCCellData* cellData; + BCData* bcData = nsnull; + + switch(aSide) { + case NS_SIDE_BOTTOM: + aRowIndex++; + case NS_SIDE_TOP: + cellData = (BCCellData*)aCellMap.GetDataAt(*this, aRowIndex, aColIndex, PR_FALSE); + if (cellData) { + bcData = &cellData->mData; + } + else { + NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error"); + // try the next row group + nsCellMap* cellMap = aCellMap.GetNextSibling(); + if (cellMap) { + cellData = (BCCellData*)cellMap->GetDataAt(*this, aRowIndex, aColIndex, PR_FALSE); + if (cellData) { + bcData = &cellData->mData; + } + else { + BCData* bcData = GetBottomMostBorder(aColIndex); + } + } + } + break; + case NS_SIDE_RIGHT: + aColIndex++; + case NS_SIDE_LEFT: + cellData = (BCCellData*)aCellMap.GetDataAt(*this, aRowIndex, aColIndex, PR_FALSE); + if (cellData) { + bcData = &cellData->mData; + } + else { + NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error"); + BCData* bcData = GetRightMostBorder(aRowIndex); + } + break; + } + return bcData; +} + +// store the aSide border segment at coord = (aRowIndex, aColIndex). For top/left, store +// the info at coord. For bottom/left store it at the adjacent location so that it is +// top/left at that location. If the new location is at the right or bottom edge of the +// table, then store it one of the special arrays (right most borders, bottom most borders). +void +nsTableCellMap::SetBCBorderEdge(PRUint8 aSide, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aRowIndex, + PRUint32 aColIndex, + PRUint32 aLength, + BCBorderOwner aOwner, + nscoord aSize, + PRBool aChanged) +{ + if (!mBCInfo) ABORT0(); + //if (aRowIndex >= 80) { + // NS_ASSERTION(PR_FALSE, "hello"); + //} + + BCCellData* cellData; + PRInt32 lastIndex, xIndex, yIndex; + PRInt32 xPos = aColIndex; + PRInt32 yPos = aRowIndex; + PRInt32 rgYPos = aRowIndex - aCellMapStart; + PRBool changed; + + switch(aSide) { + case NS_SIDE_BOTTOM: + rgYPos++; + yPos++; + case NS_SIDE_TOP: + lastIndex = xPos + aLength - 1; + for (xIndex = xPos; xIndex <= lastIndex; xIndex++) { + changed = aChanged && (xIndex == xPos); + BCData* bcData = nsnull; + cellData = (BCCellData*)aCellMap.GetDataAt(*this, rgYPos, xIndex, PR_FALSE); + if (!cellData) { + PRInt32 numRgRows = aCellMap.GetRowCount(); + if (yPos < numRgRows) { // add a dead cell data + nsRect damageArea; + cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + else { + NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error"); + // try the next non empty row group + nsCellMap* cellMap = aCellMap.GetNextSibling(); + while (cellMap && (0 == cellMap->GetRowCount())) { + cellMap = cellMap->GetNextSibling(); + } + if (cellMap) { + cellData = (BCCellData*)cellMap->GetDataAt(*this, 0, xIndex, PR_FALSE); + if (!cellData) { // add a dead cell + nsRect damageArea; + cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea); + } + } + else { // must be at the end of the table + bcData = GetBottomMostBorder(xIndex); + } + } + } + if (!bcData && cellData) { + bcData = &cellData->mData; + } + if (bcData) { + bcData->SetTopEdge(aOwner, aSize, changed); + } + else NS_ASSERTION(PR_FALSE, "program error"); + } + break; + case NS_SIDE_RIGHT: + xPos++; + case NS_SIDE_LEFT: + // since top, bottom borders were set, there should already be a cellData entry + lastIndex = rgYPos + aLength - 1; + for (yIndex = rgYPos; yIndex <= lastIndex; yIndex++) { + changed = aChanged && (yIndex == rgYPos); + cellData = (BCCellData*)aCellMap.GetDataAt(*this, yIndex, xPos, PR_FALSE); + if (cellData) { + cellData->mData.SetLeftEdge(aOwner, aSize, changed); + } + else { + NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error"); + BCData* bcData = GetRightMostBorder(yIndex + aCellMapStart); + if (bcData) { + bcData->SetLeftEdge(aOwner, aSize, changed); + } + else NS_ASSERTION(PR_FALSE, "program error"); + } + } + break; + } +} + +// store corner info (aOwner, aSubSize, aBevel). For aCorner = eTopLeft, store the info at +// (aRowIndex, aColIndex). For eTopRight, store it in the entry to the right where +// it would be top left. For eBottomRight, store it in the entry to the bottom. etc. +void +nsTableCellMap::SetBCBorderCorner(Corner aCorner, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aRowIndex, + PRUint32 aColIndex, + PRUint8 aOwner, + nscoord aSubSize, + PRBool aBevel, + PRBool aIsBottomRight) +{ + if (!mBCInfo) ABORT0(); + + if (aIsBottomRight) { + mBCInfo->mLowerRightCorner.SetCorner(aSubSize, aOwner, aBevel); + return; + } + + PRInt32 xPos = aColIndex; + PRInt32 yPos = aRowIndex; + PRInt32 rgYPos = aRowIndex - aCellMapStart; + + if (eTopRight == aCorner) { + xPos++; + } + else if (eBottomRight == aCorner) { + xPos++; + rgYPos++; + yPos++; + } + else if (eBottomLeft == aCorner) { + rgYPos++; + yPos++; + } + + BCCellData* cellData = nsnull; + BCData* bcData = nsnull; + if (GetColCount() <= xPos) { + NS_ASSERTION(xPos == GetColCount(), "program error"); + if (aIsBottomRight) { // at the bottom right corner + bcData = &mBCInfo->mLowerRightCorner; + } + else { // at the right edge of the table + bcData = GetRightMostBorder(yPos); + } + } + else { + cellData = (BCCellData*)aCellMap.GetDataAt(*this, rgYPos, xPos, PR_FALSE); + if (!cellData) { + PRInt32 numRgRows = aCellMap.GetRowCount(); + if (yPos < numRgRows) { // add a dead cell data + nsRect damageArea; + cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea); + } + else { + // try the next non empty row group + nsCellMap* cellMap = aCellMap.GetNextSibling(); + while (cellMap && (0 == cellMap->GetRowCount())) { + cellMap = cellMap->GetNextSibling(); + } + if (cellMap) { + cellData = (BCCellData*)cellMap->GetDataAt(*this, 0, xPos, PR_FALSE); + if (!cellData) { // add a dead cell + nsRect damageArea; + cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea); + } + } + else { // must be a the bottom of the table + bcData = GetBottomMostBorder(xPos); + } + } + } + } + if (!bcData && cellData) { + bcData = &cellData->mData; + } + if (bcData) { + bcData->SetCorner(aSubSize, aOwner, aBevel); + } + else NS_ASSERTION(PR_FALSE, "program error"); +} + #ifdef DEBUG void nsTableCellMap::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const @@ -540,6 +952,7 @@ void nsTableCellMap::SizeOf(nsISizeOfHandler* aHandler, mCols.SizeOf(aHandler, &voidArraySize); sum += voidArraySize - sizeof(mCols); + // XXX need to so something for mBCInfo *aResult = sum; } #endif @@ -633,7 +1046,8 @@ nsCellMap::InsertRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { PRInt32 numCols = aMap.GetColCount(); @@ -648,7 +1062,7 @@ nsCellMap::InsertRows(nsIPresContext* aPresContext, } if (!aConsiderSpans) { - ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex); + ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex, aDamageArea); return; } @@ -663,10 +1077,10 @@ nsCellMap::InsertRows(nsIPresContext* aPresContext, } if (spansCauseRebuild) { - RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, &aRows); + RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, &aRows, 0, aDamageArea); } else { - ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex); + ExpandWithRows(aPresContext, aMap, aRows, aFirstRowIndex, aDamageArea); } } @@ -675,7 +1089,8 @@ nsCellMap::RemoveRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans) + PRBool aConsiderSpans, + nsRect& aDamageArea) { PRInt32 numRows = mRows.Count(); PRInt32 numCols = aMap.GetColCount(); @@ -684,7 +1099,7 @@ nsCellMap::RemoveRows(nsIPresContext* aPresContext, return; } if (!aConsiderSpans) { - ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove); + ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea); return; } PRInt32 endRowIndex = aFirstRowIndex + aNumRowsToRemove - 1; @@ -696,50 +1111,70 @@ nsCellMap::RemoveRows(nsIPresContext* aPresContext, 0, numCols - 1); if (spansCauseRebuild) { - RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, nsnull, aNumRowsToRemove); + RebuildConsideringRows(aPresContext, aMap, aFirstRowIndex, nsnull, aNumRowsToRemove, aDamageArea); } else { - ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove); + ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea); } } -PRInt32 +void +SetDamageArea(PRInt32 aXOrigin, + PRInt32 aYOrigin, + PRInt32 aWidth, + PRInt32 aHeight, + nsRect& aDamageArea) +{ + aDamageArea.x = aXOrigin; + aDamageArea.y = aYOrigin; + aDamageArea.width = PR_MAX(1, aWidth); + aDamageArea.height = PR_MAX(1, aHeight); +} + + +CellData* nsCellMap::AppendCell(nsTableCellMap& aMap, - nsTableCellFrame& aCellFrame, + nsTableCellFrame* aCellFrame, PRInt32 aRowIndex, - PRBool aRebuildIfNecessary) + PRBool aRebuildIfNecessary, + nsRect& aDamageArea) { PRInt32 origNumMapRows = mRows.Count(); PRInt32 origNumCols = aMap.GetColCount(); PRBool zeroRowSpan; - PRInt32 rowSpan = GetRowSpanForNewCell(aCellFrame, aRowIndex, zeroRowSpan); + PRInt32 rowSpan = (aCellFrame) ? GetRowSpanForNewCell(*aCellFrame, aRowIndex, zeroRowSpan) : 1; // add new rows if necessary PRInt32 endRowIndex = aRowIndex + rowSpan - 1; if (endRowIndex >= origNumMapRows) { Grow(aMap, 1 + endRowIndex - origNumMapRows); } - // get the first null CellData in the desired row. It may be 1 past the end if there are none + // get the first null or dead CellData in the desired row. It will equal origNumCols if there are none + CellData* origData = nsnull; PRInt32 startColIndex; for (startColIndex = 0; startColIndex < origNumCols; startColIndex++) { - CellData* data = GetMapCellAt(aMap, aRowIndex, startColIndex, PR_TRUE); + CellData* data = GetDataAt(aMap, aRowIndex, startColIndex, PR_TRUE); if (!data) { - break; // we found the col + break; + } + else if (data->IsDead()) { + origData = data; + break; } } + PRBool zeroColSpan; - PRInt32 colSpan = GetColSpanForNewCell(aCellFrame, startColIndex, origNumCols, zeroColSpan); + PRInt32 colSpan = (aCellFrame) ? GetColSpanForNewCell(*aCellFrame, startColIndex, origNumCols, zeroColSpan) : 1; // if the new cell could potentially span into other rows and collide with // originating cells there, we will play it safe and just rebuild the map if (aRebuildIfNecessary && (aRowIndex < mRowCount - 1) && (rowSpan > 1)) { nsAutoVoidArray newCellArray; - newCellArray.AppendElement(&aCellFrame); - RebuildConsideringCells(aMap, &newCellArray, aRowIndex, startColIndex, PR_TRUE); - return startColIndex; + newCellArray.AppendElement(aCellFrame); + RebuildConsideringCells(aMap, &newCellArray, aRowIndex, startColIndex, PR_TRUE, aDamageArea); + return origData; } - mRowCount = PR_MAX(mRowCount, aRowIndex + 1); // add new cols to the table map if necessary @@ -749,19 +1184,29 @@ nsCellMap::AppendCell(nsTableCellMap& aMap, } // Setup CellData for this cell - CellData* origData = new CellData(&aCellFrame); - if (!origData) return startColIndex; - SetMapCellAt(aMap, *origData, aRowIndex, startColIndex, PR_TRUE); + if (origData) { + origData->Init(aCellFrame); + } + else { + origData = (aMap.mBCInfo) ? new BCCellData(aCellFrame) : new CellData(aCellFrame); if (!origData) ABORT1(origData); + SetDataAt(aMap, *origData, aRowIndex, startColIndex, PR_TRUE); + } + + SetDamageArea(startColIndex, aRowIndex, 1 + endColIndex - startColIndex, 1 + endRowIndex - aRowIndex, aDamageArea); + + if (!aCellFrame) { + return origData; + } // initialize the cell frame - aCellFrame.InitCellFrame(startColIndex); + aCellFrame->InitCellFrame(startColIndex); // Create CellData objects for the rows that this cell spans. Set // their mOrigCell to nsnull and their mSpanData to point to data. for (PRInt32 rowX = aRowIndex; rowX <= endRowIndex; rowX++) { for (PRInt32 colX = startColIndex; colX <= endColIndex; colX++) { if ((rowX != aRowIndex) || (colX != startColIndex)) { // skip orig cell data done above - CellData* cellData = GetMapCellAt(aMap, rowX, colX, PR_FALSE); + CellData* cellData = GetDataAt(aMap, rowX, colX, PR_FALSE); if (cellData) { if (cellData->IsOrig()) { NS_ASSERTION(PR_FALSE, "cannot overlap originating cell"); @@ -796,8 +1241,8 @@ nsCellMap::AppendCell(nsTableCellMap& aMap, } } else { - cellData = new CellData(nsnull); - if (!cellData) return startColIndex; + cellData = (aMap.mBCInfo) ? new BCCellData(nsnull) : new CellData(nsnull); + if (!cellData) return origData; if (rowX > aRowIndex) { cellData->SetRowSpanOffset(rowX - aRowIndex); } @@ -811,14 +1256,14 @@ nsCellMap::AppendCell(nsTableCellMap& aMap, cellData->SetZeroColSpan(PR_TRUE); } // only count the 1st spanned col of a zero col span - SetMapCellAt(aMap, *cellData, rowX, colX, (colX == startColIndex + 1)); + SetDataAt(aMap, *cellData, rowX, colX, (colX == startColIndex + 1)); } } } } - //printf("appended cell=%p row=%d \n", &aCellFrame, aRowIndex); + //printf("appended cell=%p row=%d \n", aCellFrame, aRowIndex); //aMap.Dump(); - return startColIndex; + return origData; } PRBool nsCellMap::CellsSpanOut(nsIPresContext* aPresContext, @@ -832,7 +1277,7 @@ PRBool nsCellMap::CellsSpanOut(nsIPresContext* aPresContext, while (cellFrame) { nsIAtom* frameType; cellFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableCellFrame == frameType) { + if (IS_TABLE_CELL(frameType)) { PRBool zeroSpan; PRInt32 rowSpan = GetRowSpanForNewCell((nsTableCellFrame &)*cellFrame, rowX, zeroSpan); if (rowX + rowSpan > numNewRows) { @@ -858,13 +1303,13 @@ PRBool nsCellMap::CellsSpanInOrOut(nsTableCellMap& aMap, for (PRInt32 colX = aStartColIndex; colX <= aEndColIndex; colX++) { CellData* cellData; if (aStartRowIndex > 0) { - cellData = GetMapCellAt(aMap, aStartRowIndex, colX, PR_TRUE); + cellData = GetDataAt(aMap, aStartRowIndex, colX, PR_TRUE); if (cellData && (cellData->IsRowSpan())) { return PR_TRUE; // there is a row span into the region } } if (aEndRowIndex < mRowCount - 1) { - cellData = GetMapCellAt(aMap, aEndRowIndex + 1, colX, PR_TRUE); + cellData = GetDataAt(aMap, aEndRowIndex + 1, colX, PR_TRUE); if ((cellData) && (cellData->IsRowSpan())) { return PR_TRUE; // there is a row span out of the region } @@ -872,7 +1317,7 @@ PRBool nsCellMap::CellsSpanInOrOut(nsTableCellMap& aMap, } if (aStartColIndex > 0) { for (PRInt32 rowX = aStartRowIndex; rowX <= aEndRowIndex; rowX++) { - CellData* cellData = GetMapCellAt(aMap, rowX, aStartColIndex, PR_TRUE); + CellData* cellData = GetDataAt(aMap, rowX, aStartColIndex, PR_TRUE); if (cellData && (cellData->IsColSpan())) { return PR_TRUE; // there is a col span into the region } @@ -891,7 +1336,8 @@ PRBool nsCellMap::CellsSpanInOrOut(nsTableCellMap& aMap, void nsCellMap::InsertCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore) + PRInt32 aColIndexBefore, + nsRect& aDamageArea) { if (aCellFrames.Count() == 0) return; PRInt32 numCols = aMap.GetColCount(); @@ -903,7 +1349,7 @@ void nsCellMap::InsertCells(nsTableCellMap& aMap, // get the starting col index of the 1st new cells PRInt32 startColIndex; for (startColIndex = aColIndexBefore + 1; startColIndex < numCols; startColIndex++) { - CellData* data = GetMapCellAt(aMap, aRowIndex, startColIndex, PR_TRUE); + CellData* data = GetDataAt(aMap, aRowIndex, startColIndex, PR_TRUE); if (!data || data->IsOrig()) { // stop unless it is a span break; } @@ -942,10 +1388,10 @@ void nsCellMap::InsertCells(nsTableCellMap& aMap, } if (spansCauseRebuild) { - RebuildConsideringCells(aMap, &aCellFrames, aRowIndex, startColIndex, PR_TRUE); + RebuildConsideringCells(aMap, &aCellFrames, aRowIndex, startColIndex, PR_TRUE, aDamageArea); } else { - ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, zeroRowSpan); + ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, zeroRowSpan, aDamageArea); } } @@ -953,7 +1399,8 @@ void nsCellMap::ExpandWithRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRowFrames, - PRInt32 aStartRowIndexIn) + PRInt32 aStartRowIndexIn, + nsRect& aDamageArea) { PRInt32 startRowIndex = (aStartRowIndexIn >= 0) ? aStartRowIndexIn : 0; PRInt32 numNewRows = aRowFrames.Count(); @@ -974,22 +1421,25 @@ nsCellMap::ExpandWithRows(nsIPresContext* aPresContext, while (cFrame) { nsIAtom* cFrameType; cFrame->GetFrameType(&cFrameType); - if (nsLayoutAtoms::tableCellFrame == cFrameType) { - AppendCell(aMap, (nsTableCellFrame &)*cFrame, rowX, PR_FALSE); + if (IS_TABLE_CELL(cFrameType)) { + AppendCell(aMap, (nsTableCellFrame *)cFrame, rowX, PR_FALSE, aDamageArea); } NS_IF_RELEASE(cFrameType); cFrame->GetNextSibling(&cFrame); } newRowIndex++; } + + SetDamageArea(0, startRowIndex, aMap.GetColCount(), 1 + endRowIndex - startRowIndex, aDamageArea); } void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, - PRInt32 aRowSpan, - PRBool aRowSpanIsZero) + PRInt32 aRowSpan, // same for all cells + PRBool aRowSpanIsZero, + nsRect& aDamageArea) { PRInt32 endRowIndex = aRowIndex + aRowSpan - 1; PRInt32 startColIndex = aColIndex; @@ -1000,7 +1450,7 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, // add cellData entries for the space taken up by the new cells for (PRInt32 cellX = 0; cellX < numCells; cellX++) { nsTableCellFrame* cellFrame = (nsTableCellFrame*) aCellFrames.ElementAt(cellX); - CellData* origData = new CellData(cellFrame); // the originating cell + CellData* origData = (aMap.mBCInfo) ? new BCCellData(cellFrame) : new CellData(cellFrame); // the originating cell if (!origData) return; // set the starting and ending col index for the new cell @@ -1022,7 +1472,7 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, row->InsertElementAt(nsnull, colX); CellData* data = origData; if ((rowX != aRowIndex) || (colX != startColIndex)) { - data = new CellData(nsnull); + data = (aMap.mBCInfo) ? new BCCellData(nsnull) : new CellData(nsnull); if (!data) return; if (rowX > aRowIndex) { data->SetRowSpanOffset(rowX - aRowIndex); @@ -1038,11 +1488,13 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, } } // only count 1st spanned col of colspan=0 - SetMapCellAt(aMap, *data, rowX, colX, (colX == aColIndex + 1)); + SetDataAt(aMap, *data, rowX, colX, (colX == aColIndex + 1)); } } cellFrame->InitCellFrame(startColIndex); } + PRInt32 damageHeight = (aRowSpanIsZero) ? aMap.GetColCount() - aRowIndex : aRowSpan; + SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, damageHeight, aDamageArea); PRInt32 rowX; @@ -1090,7 +1542,8 @@ void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap, PRInt32 aStartRowIndex, - PRInt32 aNumRowsToRemove) + PRInt32 aNumRowsToRemove, + nsRect& aDamageArea) { PRInt32 endRowIndex = aStartRowIndex + aNumRowsToRemove - 1; PRInt32 colCount = aMap.GetColCount(); @@ -1132,6 +1585,8 @@ void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap, mRowCount--; } aMap.RemoveColsAtEnd(); + + SetDamageArea(0, aStartRowIndex, aMap.GetColCount(), 0, aDamageArea); } PRInt32 nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, @@ -1162,15 +1617,15 @@ PRInt32 nsCellMap::GetEffectiveColSpan(nsTableCellMap& aMap, PRInt32 colX; CellData* data; PRInt32 maxCols = numColsInTable; - PRBool hitOverlap = PR_FALSE; + PRBool hitOverlap = PR_FALSE; // XXX this is not ever being set to PR_TRUE for (colX = aColIndex + 1; colX < maxCols; colX++) { - data = GetMapCellAt(aMap, aRowIndex, colX, PR_TRUE); + data = GetDataAt(aMap, aRowIndex, colX, PR_TRUE); if (data) { // for an overlapping situation get the colspan from the originating cell and // use that as the max number of cols to iterate. Since this is rare, only // pay the price of looking up the cell's colspan here. if (!hitOverlap && data->IsOverlap()) { - CellData* origData = GetMapCellAt(aMap, aRowIndex, aColIndex, PR_TRUE); + CellData* origData = GetDataAt(aMap, aRowIndex, aColIndex, PR_TRUE); if (origData->IsOrig()) { nsTableCellFrame* cellFrame = origData->GetCellFrame(); if (cellFrame) { @@ -1223,7 +1678,7 @@ PRInt32 nsCellMap::GetRowSpan(nsTableCellMap& aMap, PRInt32 rowCount = (aGetEffective) ? mRowCount : mRows.Count(); PRInt32 rowX; for (rowX = aRowIndex + 1; rowX < rowCount; rowX++) { - CellData* data = GetMapCellAt(aMap, rowX, aColIndex, PR_TRUE); + CellData* data = GetDataAt(aMap, rowX, aColIndex, PR_TRUE); if (data) { if (data->IsRowSpan()) { rowSpan++; @@ -1246,7 +1701,8 @@ PRInt32 nsCellMap::GetRowSpan(nsTableCellMap& aMap, void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, nsTableCellFrame& aCellFrame, PRInt32 aRowIndex, - PRInt32 aColIndex) + PRInt32 aColIndex, + nsRect& aDamageArea) { PRInt32 colX, rowX; @@ -1258,6 +1714,8 @@ void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, PRInt32 endRowIndex = aRowIndex + rowSpan - 1; PRInt32 endColIndex = aColIndex + colSpan - 1; + SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, 1 + endRowIndex - aRowIndex, aDamageArea); + // adjust the col counts due to the deleted cell before removing it for (colX = aColIndex; colX <= endColIndex; colX++) { nsColInfo* colInfo = aMap.GetColInfoAt(colX); @@ -1317,25 +1775,13 @@ void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, aMap.RemoveColsAtEnd(); } -void -nsCellMap::RemoveCol(PRInt32 aColIndex) -{ - PRInt32 numMapRows = mRows.Count(); - // remove the col from each of the rows - for (PRInt32 rowX = 0; rowX < numMapRows; rowX++) { - nsVoidArray* row = (nsVoidArray *)mRows.ElementAt(rowX); - CellData* data = (CellData*) row->ElementAt(aColIndex); - row->RemoveElementAt(aColIndex); - delete data; - } -} - void nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aStartRowIndex, nsVoidArray* aRowsToInsert, - PRBool aNumRowsToRemove) + PRBool aNumRowsToRemove, + nsRect& aDamageArea) { // copy the old cell map into a new array PRInt32 numOrigRows = mRows.Count(); @@ -1352,6 +1798,7 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, nsColInfo* colInfo = aMap.GetColInfoAt(colX); colInfo->mNumCellsOrig = 0; } + mRows.Clear(); mRowCount = 0; if (aRowsToInsert) { @@ -1366,7 +1813,7 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, // put in the original cell from the cell map CellData* data = (CellData*) row->ElementAt(colX); if (data && data->IsOrig()) { - AppendCell(aMap, *data->GetCellFrame(), rowX, PR_FALSE); + AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea); } } } @@ -1382,8 +1829,8 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, while (cFrame) { nsIAtom* cFrameType; cFrame->GetFrameType(&cFrameType); - if (nsLayoutAtoms::tableCellFrame == cFrameType) { - AppendCell(aMap, (nsTableCellFrame &)*cFrame, rowX, PR_FALSE); + if (IS_TABLE_CELL(cFrameType)) { + AppendCell(aMap, (nsTableCellFrame *)cFrame, rowX, PR_FALSE, aDamageArea); } NS_IF_RELEASE(cFrameType); cFrame->GetNextSibling(&cFrame); @@ -1405,7 +1852,7 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, // put in the original cell from the cell map CellData* data = (CellData*) row->ElementAt(colX); if (data && data->IsOrig()) { - AppendCell(aMap, *data->GetCellFrame(), rowX, PR_FALSE); + AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea); } } rowX++; @@ -1422,13 +1869,16 @@ nsCellMap::RebuildConsideringRows(nsIPresContext* aPresContext, delete row; } delete [] origRows; + + SetDamageArea(0, 0, aMap.GetColCount(), GetRowCount(), aDamageArea); } void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, nsVoidArray* aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, - PRBool aInsert) + PRBool aInsert, + nsRect& aDamageArea) { // copy the old cell map into a new array PRInt32 mRowCountOrig = mRowCount; @@ -1441,6 +1891,7 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, nsVoidArray* row = (nsVoidArray *)mRows.ElementAt(rowX); origRows[rowX] = row; } + // reinitialize data members mRows.Clear(); mRowCount = 0; @@ -1457,7 +1908,7 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) { nsTableCellFrame* cell = (nsTableCellFrame*)aCellFrames->ElementAt(cellX); if (cell) { - AppendCell(aMap, *cell, rowX, PR_FALSE); + AppendCell(aMap, cell, rowX, PR_FALSE, aDamageArea); } } } @@ -1468,7 +1919,7 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, // put in the original cell from the cell map CellData* data = (CellData*) row->ElementAt(colX); if (data && data->IsOrig()) { - AppendCell(aMap, *data->GetCellFrame(), rowX, PR_FALSE); + AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea); } } } @@ -1490,11 +1941,14 @@ void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, delete row; } delete [] origRows; + + SetDamageArea(0, 0, aMap.GetColCount(), GetRowCount(), aDamageArea); } void nsCellMap::RemoveCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex) + PRInt32 aRowIndex, + nsRect& aDamageArea) { PRInt32 numRows = mRows.Count(); if ((aRowIndex < 0) || (aRowIndex >= numRows)) { @@ -1506,7 +1960,7 @@ void nsCellMap::RemoveCell(nsTableCellMap& aMap, // get the starting col index of the cell to remove PRInt32 startColIndex; for (startColIndex = 0; startColIndex < numCols; startColIndex++) { - CellData* data = GetMapCellAt(aMap, aRowIndex, startColIndex, PR_FALSE); + CellData* data = GetDataAt(aMap, aRowIndex, startColIndex, PR_FALSE); if (data && (data->IsOrig()) && (aCellFrame == data->GetCellFrame())) { break; // we found the col index } @@ -1523,15 +1977,15 @@ void nsCellMap::RemoveCell(nsTableCellMap& aMap, // no extraneous cols at the end. The same is true for removing rows. if (spansCauseRebuild) { - RebuildConsideringCells(aMap, nsnull, aRowIndex, startColIndex, PR_FALSE); + RebuildConsideringCells(aMap, nsnull, aRowIndex, startColIndex, PR_FALSE, aDamageArea); } else { - ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex); + ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex, aDamageArea); } } #ifdef NS_DEBUG -void nsCellMap::Dump() const +void nsCellMap::Dump(PRBool aIsBorderCollapse) const { printf("\n ***** START GROUP CELL MAP DUMP ***** %p\n", this); PRInt32 mapRowCount = mRows.Count(); @@ -1566,6 +2020,33 @@ void nsCellMap::Dump() const printf("---- "); } } + if (aIsBorderCollapse) { + nscoord size; + BCBorderOwner owner; + PRUint8 side; + PRBool segStart; + PRPackedBool bevel; + for (PRInt32 i = 0; i <= 2; i++) { + printf("\n "); + for (colIndex = 0; colIndex < colCount; colIndex++) { + BCCellData* cd = (BCCellData *)row->ElementAt(colIndex); + if (cd) { + if (0 == i) { + size = cd->mData.GetTopEdge(owner, segStart); + printf("t=%d%d%d ", size, owner, segStart); + } + else if (1 == i) { + size = cd->mData.GetLeftEdge(owner, segStart); + printf("l=%d%d%d ", size, owner, segStart); + } + else { + size = cd->mData.GetCorner(side, bevel); + printf("c=%d%d%d ", size, side, bevel); + } + } + } + } + } printf("\n"); } @@ -1614,7 +2095,7 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, PRInt32 aColIndex) { PRInt32 numColsInTable = aMap.GetColCount(); - CellData* data = GetMapCellAt(aMap, aRowIndex, aColIndex, PR_FALSE); + CellData* data = GetDataAt(aMap, aRowIndex, aColIndex, PR_FALSE); if (!data) return; nsTableCellFrame* cell = (data->IsOrig()) ? data->GetCellFrame() : nsnull; @@ -1640,7 +2121,7 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, PRBool cellsOrig = PR_FALSE; if (colX >= aColIndex + MIN_NUM_COLS_FOR_ZERO_COLSPAN - 1) { for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { - CellData* cellData = GetMapCellAt(aMap, rowX, colX, PR_FALSE); + CellData* cellData = GetDataAt(aMap, rowX, colX, PR_FALSE); if (cellData && cellData->IsOrig()) { cellsOrig = PR_TRUE; break; // there are cells in this col, so don't consider it @@ -1650,9 +2131,9 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, if (cellsOrig) break; for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { if ((colX > aColIndex) || (rowX > aRowIndex)) { - CellData* oldData = GetMapCellAt(aMap, rowX, colX, PR_FALSE); + CellData* oldData = GetDataAt(aMap, rowX, colX, PR_FALSE); if (!oldData) { - CellData* newData = new CellData(nsnull); + CellData* newData = (aMap.mBCInfo) ? new BCCellData(nsnull) : new CellData(nsnull); if (!newData) return; if (colX > aColIndex) { newData->SetColSpanOffset(colX - aColIndex); @@ -1663,7 +2144,7 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, newData->SetZeroRowSpan(PR_TRUE); } // colspan=0 is only counted as spanning the 1st col to the right of its origin - SetMapCellAt(aMap, *newData, rowX, colX, (colX == aColIndex + 1)); + SetDataAt(aMap, *newData, rowX, colX, (colX == aColIndex + 1)); } } } @@ -1671,10 +2152,10 @@ nsCellMap::AdjustForZeroSpan(nsTableCellMap& aMap, } CellData* -nsCellMap::GetMapCellAt(nsTableCellMap& aMap, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aUpdateZeroSpan) +nsCellMap::GetDataAt(nsTableCellMap& aMap, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan) { PRInt32 numColsInTable = aMap.GetColCount(); if ((aMapRowIndex < 0) || (aMapRowIndex >= mRows.Count())) { @@ -1714,7 +2195,7 @@ nsCellMap::GetMapCellAt(nsTableCellMap& aMap, PRInt32 prevColX = aColIndex - 1; // find the last non null data in the same row for ( ; prevColX > 0; prevColX--) { - CellData* prevData = GetMapCellAt(aMap, aMapRowIndex, prevColX, PR_FALSE); + CellData* prevData = GetDataAt(aMap, aMapRowIndex, prevColX, PR_FALSE); if (prevData) { if (prevData->IsZeroColSpan()) { PRInt32 colIndex = prevColX - prevData->GetColSpanOffset(); @@ -1729,29 +2210,18 @@ nsCellMap::GetMapCellAt(nsTableCellMap& aMap, // if zero span adjustments were made the data may be available now if (!data && didZeroExpand) { - data = GetMapCellAt(aMap, aMapRowIndex, aColIndex, PR_FALSE); + data = GetDataAt(aMap, aMapRowIndex, aColIndex, PR_FALSE); } } return data; } -CellData* -nsCellMap::GetCellAt(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex) -{ - if ((0 > aRowIndex) || (aRowIndex >= mRowCount)) { - return nsnull; - } - return GetMapCellAt(aMap, aRowIndex, aColIndex, PR_TRUE); -} - // only called if the cell at aMapRowIndex, aColIndex is null -void nsCellMap::SetMapCellAt(nsTableCellMap& aMap, - CellData& aNewCell, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aCountZeroSpanAsSpan) +void nsCellMap::SetDataAt(nsTableCellMap& aMap, + CellData& aNewCell, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aCountZeroSpanAsSpan) { nsVoidArray* row = (nsVoidArray *)(mRows.SafeElementAt(aMapRowIndex)); if (row) { @@ -1777,9 +2247,9 @@ void nsCellMap::SetMapCellAt(nsTableCellMap& aMap, colInfo->mNumCellsSpan++; } } - else NS_ASSERTION(PR_FALSE, "SetMapCellAt called with col index > table map num cols"); + else NS_ASSERTION(PR_FALSE, "SetDataAt called with col index > table map num cols"); } - else NS_ASSERTION(PR_FALSE, "SetMapCellAt called with row index > num rows"); + else NS_ASSERTION(PR_FALSE, "SetDataAt called with row index > num rows"); } nsTableCellFrame* @@ -1792,7 +2262,7 @@ nsCellMap::GetCellInfoAt(nsTableCellMap& aMap, if (aOriginates) { *aOriginates = PR_FALSE; } - CellData* data = GetCellAt(aMap, aRowX, aColX); + CellData* data = GetDataAt(aMap, aRowX, aColX, PR_TRUE); nsTableCellFrame* cellFrame = nsnull; if (data) { if (data->IsOrig()) { @@ -1824,7 +2294,7 @@ PRBool nsCellMap::RowIsSpannedInto(nsTableCellMap& aMap, return PR_FALSE; } for (PRInt32 colIndex = 0; colIndex < numColsInTable; colIndex++) { - CellData* cd = GetCellAt(aMap, aRowIndex, colIndex); + CellData* cd = GetDataAt(aMap, aRowIndex, colIndex, PR_TRUE); if (cd) { // there's really a cell at (aRowIndex, colIndex) if (cd->IsSpan()) { // the cell at (aRowIndex, colIndex) is the result of a span if (cd->IsRowSpan() && GetCellFrame(aRowIndex, colIndex, *cd, PR_TRUE)) { // XXX why the last check @@ -1846,9 +2316,9 @@ PRBool nsCellMap::RowHasSpanningCells(nsTableCellMap& aMap, if (aRowIndex != mRowCount - 1) { // aRowIndex is not the last row, so we check the next row after aRowIndex for spanners for (PRInt32 colIndex = 0; colIndex < numColsInTable; colIndex++) { - CellData* cd = GetCellAt(aMap, aRowIndex, colIndex); + CellData* cd = GetDataAt(aMap, aRowIndex, colIndex, PR_TRUE); if (cd && (cd->IsOrig())) { // cell originates - CellData* cd2 = GetCellAt(aMap, aRowIndex + 1, colIndex); + CellData* cd2 = GetDataAt(aMap, aRowIndex + 1, colIndex, PR_TRUE); if (cd2 && cd2->IsRowSpan()) { // cd2 is spanned by a row if (cd->GetCellFrame() == GetCellFrame(aRowIndex + 1, colIndex, *cd2, PR_TRUE)) { return PR_TRUE; @@ -1869,9 +2339,9 @@ PRBool nsCellMap::ColHasSpanningCells(nsTableCellMap& aMap, return PR_FALSE; for (PRInt32 rowIndex = 0; rowIndex < mRowCount; rowIndex++) { - CellData* cd = GetCellAt(aMap, rowIndex, aColIndex); + CellData* cd = GetDataAt(aMap, rowIndex, aColIndex, PR_TRUE); if (cd && (cd->IsOrig())) { // cell originates - CellData* cd2 = GetCellAt(aMap, rowIndex + 1, aColIndex); + CellData* cd2 = GetDataAt(aMap, rowIndex + 1, aColIndex, PR_TRUE); if (cd2 && cd2->IsColSpan()) { // cd2 is spanned by a col if (cd->GetCellFrame() == GetCellFrame(rowIndex + 1, aColIndex, *cd2, PR_FALSE)) { return PR_TRUE; diff --git a/mozilla/layout/tables/nsCellMap.h b/mozilla/layout/tables/nsCellMap.h index 89211aa5f99..4b1b6df3b7f 100644 --- a/mozilla/layout/tables/nsCellMap.h +++ b/mozilla/layout/tables/nsCellMap.h @@ -40,6 +40,8 @@ #include "nscore.h" #include "celldata.h" #include "nsVoidArray.h" +#include "nsRect.h" + class nsTableColFrame; class nsTableCellFrame; class nsIPresContext; @@ -58,10 +60,27 @@ struct nsColInfo PRInt32 aNumCellsSpan); }; +enum Corner +{ + eTopLeft = 0, + eTopRight = 1, + eBottomRight = 2, + eBottomLeft = 3 +}; + +struct BCInfo +{ + nsVoidArray mRightBorders; + nsVoidArray mBottomBorders; + BCData mLowerRightCorner; +}; + class nsTableCellMap { public: - nsTableCellMap(nsIPresContext* aPresContext, nsTableFrame& aTableFrame); + nsTableCellMap(nsIPresContext* aPresContext, + nsTableFrame& aTableFrame, + PRBool aBorderCollapse); /** destructor * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED @@ -69,43 +88,52 @@ public: ~nsTableCellMap(); void RemoveGroupCellMap(nsTableRowGroupFrame* aRowGroup); + void InsertGroupCellMap(nsTableRowGroupFrame& aNewRowGroup, nsTableRowGroupFrame*& aPrevRowGroup); + nsCellMap* GetMapFor(nsTableRowGroupFrame& aRowGroup); + nsTableCellFrame* GetCellFrame(PRInt32 aRowIndex, PRInt32 aColIndex, CellData& aData, PRBool aUseRowIfOverlap) const; /** return the CellData for the cell at (aTableRowIndex, aTableColIndex) */ - CellData* GetCellAt(PRInt32 aRowIndex, - PRInt32 aColIndex); + CellData* GetDataAt(PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan = PR_TRUE); nsColInfo* GetColInfoAt(PRInt32 aColIndex); /** append the cellFrame at the end of the row at aRowIndex and return the col index */ - PRInt32 AppendCell(nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex, - PRBool aRebuildIfNecessary); + CellData* AppendCell(nsTableCellFrame& aCellFrame, + PRInt32 aRowIndex, + PRBool aRebuildIfNecessary, + nsRect& aDamageArea); void InsertCells(nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore); + PRInt32 aColIndexBefore, + nsRect& aDamageArea); void RemoveCell(nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex); + PRInt32 aRowIndex, + nsRect& aDamageArea); void InsertRows(nsIPresContext* aPresContext, nsTableRowGroupFrame& aRowGroup, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); void RemoveRows(nsIPresContext* aPresContext, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex); PRInt32 GetNumCellsOriginatingInCol(PRInt32 aColIndex) const; @@ -134,6 +162,32 @@ public: PRBool ColIsSpannedInto(PRInt32 aColIndex); PRBool ColHasSpanningCells(PRInt32 aColIndex); + BCData* GetBCData(PRUint8 aSide, + nsCellMap& aCellMap, + PRUint32 aYPos, + PRUint32 aXPos, + PRBool aIsLowerRight = PR_FALSE); + + void SetBCBorderEdge(PRUint8 aEdge, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aYPos, + PRUint32 aXPos, + PRUint32 aLength, + BCBorderOwner aOwner, + nscoord aSize, + PRBool aChanged); + + void SetBCBorderCorner(Corner aCorner, + nsCellMap& aCellMap, + PRUint32 aCellMapStart, + PRUint32 aYPos, + PRUint32 aXPos, + PRUint8 aOwner, + nscoord aSubSize, + PRBool aBevel, + PRBool aIsBottomRight = PR_FALSE); + /** dump a representation of the cell map to stdout for debugging */ #ifdef NS_DEBUG void Dump(char* aString = nsnull) const; @@ -144,13 +198,21 @@ public: #endif protected: + BCData* GetRightMostBorder(PRInt32 aRowIndex); + BCData* GetBottomMostBorder(PRInt32 aColIndex); + friend class nsCellMap; + friend class BCMapCellIterator; + friend class BCMapBorderIterator; void InsertGroupCellMap(nsCellMap* aPrevMap, nsCellMap& aNewMap); + void DeleteRightBottomBorders(); - nsTableFrame& mTableFrame; + nsTableFrame& mTableFrame; nsAutoVoidArray mCols; - nsCellMap* mFirstMap; + nsCellMap* mFirstMap; + // border collapsing info + BCInfo* mBCInfo; }; /** nsCellMap is a support class for nsTablePart. @@ -190,40 +252,38 @@ public: CellData& aData, PRBool aUseRowSpanIfOverlap) const; - /** return the CellData for the cell at (aTableRowIndex, aTableColIndex) */ - CellData* GetCellAt(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex); - /** append the cellFrame at the end of the row at aRowIndex and return the col index */ - PRInt32 AppendCell(nsTableCellMap& aMap, - nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex, - PRBool aRebuildIfNecessary); + CellData* AppendCell(nsTableCellMap& aMap, + nsTableCellFrame* aCellFrame, + PRInt32 aRowIndex, + PRBool aRebuildIfNecessary, + nsRect& aDamageArea); void InsertCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, - PRInt32 aColIndexBefore); + PRInt32 aColIndexBefore, + nsRect& aDamageArea); void RemoveCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, - PRInt32 aRowIndex); - - void RemoveCol(PRInt32 aColIndex); + PRInt32 aRowIndex, + nsRect& aDamageArea); void InsertRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRows, PRInt32 aFirstRowIndex, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); void RemoveRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aFirstRowIndex, PRInt32 aNumRowsToRemove, - PRBool aConsiderSpans); + PRBool aConsiderSpans, + nsRect& aDamageArea); PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const; PRInt32 GetNumCellsOriginatingInCol(PRInt32 aColIndex) const; @@ -246,9 +306,20 @@ public: PRBool ColHasSpanningCells(nsTableCellMap& aMap, PRInt32 aColIndex); + PRInt32 GetRowSpan(nsTableCellMap& aMap, + PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool aGetEffective, + PRBool& aIsZeroRowSpan); + + PRInt32 GetEffectiveColSpan(nsTableCellMap& aMap, + PRInt32 aRowIndex, + PRInt32 aColIndex, + PRBool& aIsZeroColSpan); + /** dump a representation of the cell map to stdout for debugging */ #ifdef NS_DEBUG - void Dump() const; + void Dump(PRBool aIsBorderCollapse) const; #endif #ifdef DEBUG @@ -257,6 +328,9 @@ public: protected: friend class nsTableCellMap; + friend class BCMapCellIterator; + friend class BCMapBorderIterator; + friend class nsTableFrame; PRBool Grow(nsTableCellMap& aMap, PRInt32 aNumRows, @@ -266,51 +340,57 @@ protected: PRInt32 aNumCols); /** assign aCellData to the cell at (aRow,aColumn) */ - void SetMapCellAt(nsTableCellMap& aMap, - CellData& aCellData, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aCountZeroSpanAsSpan); + void SetDataAt(nsTableCellMap& aMap, + CellData& aCellData, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aCountZeroSpanAsSpan); - CellData* GetMapCellAt(nsTableCellMap& aMap, - PRInt32 aMapRowIndex, - PRInt32 aColIndex, - PRBool aUpdateZeroSpan); + CellData* GetDataAt(nsTableCellMap& aMap, + PRInt32 aMapRowIndex, + PRInt32 aColIndex, + PRBool aUpdateZeroSpan); PRInt32 GetNumCellsIn(PRInt32 aColIndex) const; void ExpandWithRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, nsVoidArray& aRowFrames, - PRInt32 aStartRowIndex); + PRInt32 aStartRowIndex, + nsRect& aDamageArea); void ExpandWithCells(nsTableCellMap& aMap, nsVoidArray& aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, PRInt32 aRowSpan, - PRBool aRowSpanIsZero); + PRBool aRowSpanIsZero, + nsRect& aDamageArea); void ShrinkWithoutRows(nsTableCellMap& aMap, PRInt32 aFirstRowIndex, - PRInt32 aNumRowsToRemove); + PRInt32 aNumRowsToRemove, + nsRect& aDamageArea); void ShrinkWithoutCell(nsTableCellMap& aMap, nsTableCellFrame& aCellFrame, PRInt32 aRowIndex, - PRInt32 aColIndex); + PRInt32 aColIndex, + nsRect& aDamageArea); void RebuildConsideringRows(nsIPresContext* aPresContext, nsTableCellMap& aMap, PRInt32 aStartRowIndex, nsVoidArray* aRowsToInsert, - PRInt32 aNumRowsToRemove = 0); + PRInt32 aNumRowsToRemove, + nsRect& aDamageArea); void RebuildConsideringCells(nsTableCellMap& aMap, nsVoidArray* aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, - PRBool aInsert); + PRBool aInsert, + nsRect& aDamageArea); PRBool CellsSpanOut(nsIPresContext* aPresContext, nsVoidArray& aNewRows); @@ -327,21 +407,10 @@ protected: PRBool CreateEmptyRow(PRInt32 aRowIndex, PRInt32 aNumCols); - PRInt32 GetRowSpan(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex, - PRBool aGetEffective, - PRBool& aIsZeroRowSpan); - PRInt32 GetRowSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, PRInt32 aRowIndex, PRBool& aIsZeroRowSpan); - PRInt32 GetEffectiveColSpan(nsTableCellMap& aMap, - PRInt32 aRowIndex, - PRInt32 aColIndex, - PRBool& aIsZeroColSpan); - PRInt32 GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, PRInt32 aColIndex, PRInt32 aNumColsInTable, diff --git a/mozilla/layout/tables/nsITableLayoutStrategy.h b/mozilla/layout/tables/nsITableLayoutStrategy.h index 0349e3924d8..bae856d64b4 100644 --- a/mozilla/layout/tables/nsITableLayoutStrategy.h +++ b/mozilla/layout/tables/nsITableLayoutStrategy.h @@ -76,7 +76,8 @@ public: * @param aPixelToTwips - the number of twips in a pixel. * @return - the basis for percent calculations */ - virtual nscoord CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState, + virtual nscoord CalcPctAdjTableWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nscoord aAvailWidth, float aPixelToTwips)=0; diff --git a/mozilla/layout/tables/nsTableCellFrame.cpp b/mozilla/layout/tables/nsTableCellFrame.cpp index 2c0e87b11de..06ca717b3bd 100644 --- a/mozilla/layout/tables/nsTableCellFrame.cpp +++ b/mozilla/layout/tables/nsTableCellFrame.cpp @@ -100,7 +100,7 @@ nsTableCellFrame::GetNextCell() const while (childFrame) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { return (nsTableCellFrame*)childFrame; } childFrame->GetNextSibling(&childFrame); @@ -439,7 +439,7 @@ nsTableCellFrame::Paint(nsIPresContext* aPresContext, if ((NS_SUCCEEDED(rv)) && tableFrame) { const nsStyleTableBorder* tableStyle; tableFrame->GetStyleData(eStyleStruct_TableBorder, ((const nsStyleStruct *&)tableStyle)); - if (NS_STYLE_BORDER_SEPARATE == tableFrame->GetBorderCollapseStyle()) { + if (!tableFrame->IsBorderCollapse()) { nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, aDirtyRect, rect, *myBorder, mStyleContext, skipSides); } @@ -582,10 +582,9 @@ void nsTableCellFrame::VerticallyAlignChild(nsIPresContext* aPresContex const nsStyleTextReset* textStyle = (const nsStyleTextReset*)mStyleContext->GetStyleData(eStyleStruct_TextReset); /* XXX: remove tableFrame when border-collapse inherits */ - nsTableFrame* tableFrame = nsnull; - (void) nsTableFrame::GetTableFrame(this, tableFrame); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); nsMargin borderPadding; - GetCellBorder (borderPadding, tableFrame); + GetBorderWidth (p2t, borderPadding); nsMargin padding = nsTableFrame::GetPadding(aReflowState, this); borderPadding += padding; @@ -822,13 +821,13 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, PRBool contentEmptyBeforeReflow = GetContentEmpty(); /* XXX: remove tableFrame when border-collapse inherits */ - nsTableFrame* tableFrame=nsnull; - rv = nsTableFrame::GetTableFrame(this, tableFrame); + nsTableFrame* tableFrame = nsnull; + rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(NS_ERROR_NULL_POINTER); nsTableFrame* tableFrameFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); nsMargin borderPadding = aReflowState.mComputedPadding; nsMargin border; - GetCellBorder(border, tableFrame); + GetBorderWidth(p2t, border); if ((NS_UNCONSTRAINEDSIZE == availSize.width) || !contentEmptyBeforeReflow) { borderPadding += border; } @@ -1130,51 +1129,6 @@ NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext, return NS_OK; } -/** - * - * Update the border style to map to the HTML border style - * - */ -void nsTableCellFrame::MapHTMLBorderStyle(nsIPresContext* aPresContext, - nsStyleBorder& aBorderStyle, - nsTableFrame* aTableFrame) -{ - //adjust the border style based on the table rules attribute - - /* The RULES code below has been disabled because collapsing borders have been disabled - and RULES depend on collapsing borders - - const nsStyleTable* tableStyle; - aTableFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle); - - switch (tableStyle->mRules) - { - case NS_STYLE_TABLE_RULES_NONE: - aBorderStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_NONE); - break; - - case NS_STYLE_TABLE_RULES_COLS: - aBorderStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - break; - - case NS_STYLE_TABLE_RULES_ROWS: - aBorderStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_NONE); - aBorderStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_NONE); - break; - - default: - // do nothing for "GROUPS" or "ALL" or for any illegal value - // "GROUPS" will be handled in nsTableFrame::ProcessGroupRules - break; - } - */ -} - - PRBool nsTableCellFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult) { if (aValue.GetUnit() == eHTMLUnit_Pixel) @@ -1401,13 +1355,16 @@ nsTableCellFrame::GetNextCellInColumn(nsITableCellLayout **aCellLayout) } nsresult -NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) +NS_NewTableCellFrame(nsIPresShell* aPresShell, + PRBool aIsBorderCollapse, + nsIFrame** aNewFrame) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } - nsTableCellFrame* it = new (aPresShell) nsTableCellFrame; + nsTableCellFrame* it = (aIsBorderCollapse) ? new (aPresShell) nsBCTableCellFrame + : new (aPresShell) nsTableCellFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } @@ -1415,27 +1372,16 @@ NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) return NS_OK; } - - -/* ----- methods from CellLayoutData ----- */ - -void -nsTableCellFrame::GetCellBorder(nsMargin& aBorder, - nsTableFrame* aTableFrame) +nsMargin* +nsTableCellFrame::GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const { aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0; - if (nsnull==aTableFrame) { - return; - } - if (NS_STYLE_BORDER_SEPARATE == aTableFrame->GetBorderCollapseStyle()) { - const nsStyleBorder* borderData; - GetStyleData(eStyleStruct_Border, (const nsStyleStruct*&)borderData); - borderData->GetBorder(aBorder); - } - else { - NS_ASSERTION(PR_FALSE, "not implemented"); - } + const nsStyleBorder* borderData; + GetStyleData(eStyleStruct_Border, (const nsStyleStruct*&)borderData); + borderData->GetBorder(aBorder); + return &aBorder; } NS_IMETHODIMP @@ -1502,3 +1448,100 @@ nsTableCellFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const return NS_OK; } #endif + +// nsBCTableCellFrame + +nsBCTableCellFrame::nsBCTableCellFrame() +:nsTableCellFrame() +{ + mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0; +} + +nsBCTableCellFrame::~nsBCTableCellFrame() +{ +} + +NS_IMETHODIMP +nsBCTableCellFrame::GetFrameType(nsIAtom** aType) const +{ + NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer"); + *aType = nsLayoutAtoms::bcTableCellFrame; + NS_ADDREF(*aType); + return NS_OK; +} + +#ifdef DEBUG +NS_IMETHODIMP +nsBCTableCellFrame::GetFrameName(nsAString& aResult) const +{ + return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult); +} +#endif + +void +nsBCTableCellFrame::SetBorderWidth(const nsMargin& aBorder) +{ + mTopBorder = aBorder.top; + mRightBorder = aBorder.right; + mBottomBorder = aBorder.bottom; + mLeftBorder = aBorder.left; +} + +nsMargin* +nsBCTableCellFrame::GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const +{ + aBorder.top = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mTopBorder) : mTopBorder; + aBorder.right = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mRightBorder) : mRightBorder; + aBorder.bottom = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mBottomBorder): mBottomBorder; + aBorder.left = (aPixelsToTwips) ? NSToCoordRound(aPixelsToTwips * mLeftBorder): mLeftBorder; + return &aBorder; +} + +nscoord +nsBCTableCellFrame::GetBorderWidth(PRUint8 aSide) const +{ + switch(aSide) { + case NS_SIDE_TOP: + return (PRUint8)mTopBorder; + case NS_SIDE_RIGHT: + return (PRUint8)mRightBorder; + case NS_SIDE_BOTTOM: + return (PRUint8)mBottomBorder; + default: + return (PRUint8)mLeftBorder; + } +} + +void +nsBCTableCellFrame::SetBorderWidth(PRUint8 aSide, + nscoord aValue) +{ + switch(aSide) { + case NS_SIDE_TOP: + mTopBorder = aValue; + break; + case NS_SIDE_RIGHT: + mRightBorder = aValue; + break; + case NS_SIDE_BOTTOM: + mBottomBorder = aValue; + break; + default: + mLeftBorder = aValue; + } +} + +#ifdef DEBUG +NS_IMETHODIMP +nsBCTableCellFrame::SizeOf(nsISizeOfHandler* aHandler, + PRUint32* aResult) const +{ + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + PRUint32 sum = sizeof(*this); + *aResult = sum; + return NS_OK; +} +#endif diff --git a/mozilla/layout/tables/nsTableCellFrame.h b/mozilla/layout/tables/nsTableCellFrame.h index bba34cedb09..06474bfe35e 100644 --- a/mozilla/layout/tables/nsTableCellFrame.h +++ b/mozilla/layout/tables/nsTableCellFrame.h @@ -43,6 +43,7 @@ #include "nsTableRowFrame.h" // need to actually include this here to inline GetRowIndex #include "nsIStyleContext.h" #include "nsIPercentHeightObserver.h" +#include "nsLayoutAtoms.h" class nsTableFrame; class nsHTMLValue; @@ -266,6 +267,9 @@ public: nsTableCellFrame* GetNextCell() const; + virtual nsMargin* GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const; + protected: /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -277,18 +281,11 @@ private: // All these methods are support methods for RecalcLayoutData nsIFrame* GetFrameAt(nsVoidArray* aList, PRInt32 aIndex); - //XXX: aTableFrame can be removed as soon as border-collapse inherits correctly - void GetCellBorder(nsMargin &aBorder, nsTableFrame *aTableFrame); - protected: friend class nsTableRowFrame; void MapBorderPadding(nsIPresContext* aPresContext); - void MapHTMLBorderStyle(nsIPresContext* aPresContext, - nsStyleBorder& aBorderStyle, - nsTableFrame* aTableFrame); - void MapVAlignAttribute(nsIPresContext* aPresContext, nsTableFrame *aTableFrame); void MapHAlignAttribute(nsIPresContext* aPresContext, nsTableFrame *aTableFrame); @@ -421,6 +418,41 @@ inline void nsTableCellFrame::SetLastBlockHeight(nscoord aValue) { mBits.mLastBlockHeight = aValue; } + +// nsBCTableCellFrame +class nsBCTableCellFrame : public nsTableCellFrame +{ +public: + + nsBCTableCellFrame(); + + ~nsBCTableCellFrame(); + + NS_IMETHOD GetFrameType(nsIAtom** aType) const; + + virtual nsMargin* GetBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) const; + nscoord GetBorderWidth(PRUint8 aSide) const; + + void SetBorderWidth(const nsMargin& aBorder); + void SetBorderWidth(PRUint8 aSide, nscoord aPixelValue); + +#ifdef DEBUG + NS_IMETHOD GetFrameName(nsAString& aResult) const; + NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const; +#endif + +private: + + PRUint32 mTopBorder: 8; + PRUint32 mRightBorder: 8; + PRUint32 mBottomBorder: 8; + PRUint32 mLeftBorder: 8; +}; + +#define IS_TABLE_CELL(frameType)\ +((nsLayoutAtoms::tableCellFrame == frameType) || (nsLayoutAtoms::bcTableCellFrame == frameType)) + #endif diff --git a/mozilla/layout/tables/nsTableColFrame.cpp b/mozilla/layout/tables/nsTableColFrame.cpp index ba970d16d9b..38368963da6 100644 --- a/mozilla/layout/tables/nsTableColFrame.cpp +++ b/mozilla/layout/tables/nsTableColFrame.cpp @@ -113,7 +113,8 @@ nsStyleCoord nsTableColFrame::GetStyleWidth() const nsStyleCoord styleWidth = position->mWidth; // the following should not be necessary since html.css defines table-col and // :table-col to inherit. However, :table-col is not inheriting properly - if (eStyleUnit_Auto == styleWidth.GetUnit()) { + if (eStyleUnit_Auto == styleWidth.GetUnit() || + eStyleUnit_Inherit == styleWidth.GetUnit()) { nsIFrame* parent; GetParent(&parent); nsCOMPtr styleContext; @@ -305,6 +306,22 @@ nsTableColFrame::Init(nsIPresContext* aPresContext, return rv; } +nsTableColFrame* +nsTableColFrame::GetNextCol() const +{ + nsIFrame* childFrame; + GetNextSibling(&childFrame); + while (childFrame) { + nsCOMPtr frameType; + childFrame->GetFrameType(getter_AddRefs(frameType)); + if (nsLayoutAtoms::tableColFrame == frameType.get()) { + return (nsTableColFrame*)childFrame; + } + childFrame->GetNextSibling(&childFrame); + } + return nsnull; +} + NS_IMETHODIMP nsTableColFrame::GetFrameType(nsIAtom** aType) const { diff --git a/mozilla/layout/tables/nsTableColFrame.h b/mozilla/layout/tables/nsTableColFrame.h index ba2d0042b04..505a84b3726 100644 --- a/mozilla/layout/tables/nsTableColFrame.h +++ b/mozilla/layout/tables/nsTableColFrame.h @@ -102,6 +102,8 @@ public: void SetColIndex (PRInt32 aColIndex); + nsTableColFrame* GetNextCol() const; + NS_IMETHOD Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, @@ -164,6 +166,11 @@ public: void ResetSizingInfo(); + nscoord GetLeftBorderWidth(float* aPixelsToTwips = nsnull); + void SetLeftBorderWidth(nscoord aWidth); + nscoord GetRightBorderWidth(float* aPixelsToTwips = nsnull); + void SetRightBorderWidth(nscoord aWidth); + void Dump(PRInt32 aIndent); protected: @@ -172,17 +179,46 @@ protected: ~nsTableColFrame(); // the starting index of the column (starting at 0) that this col object represents // - PRInt32 mColIndex; + PRUint32 mColIndex: 16; + PRUint32 mLeftBorderWidth: 8; // stored as pixels + PRUint32 mRightBorderWidth: 8; // stored as pixels + // Widths including MIN_CON, DES_CON, FIX_CON, MIN_ADJ, DES_ADJ, FIX_ADJ, PCT, PCT_ADJ, MIN_PRO, FINAL // Widths including MIN_CON, DES_CON, FIX_CON, MIN_ADJ, DES_ADJ, FIX_ADJ, PCT, PCT_ADJ, MIN_PRO, FINAL // XXX these could be stored as pixels and converted to twips for a savings of 10 x 2 bytes. nscoord mWidths[NUM_WIDTHS]; }; inline PRInt32 nsTableColFrame::GetColIndex() const -{ return mColIndex; } +{ + return mColIndex; +} inline void nsTableColFrame::SetColIndex (PRInt32 aColIndex) -{ mColIndex = aColIndex; } +{ + mColIndex = aColIndex; +} + +inline nscoord nsTableColFrame::GetLeftBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mLeftBorderWidth) : mLeftBorderWidth; + return width; +} + +inline void nsTableColFrame::SetLeftBorderWidth(nscoord aWidth) +{ + mLeftBorderWidth = aWidth; +} + +inline nscoord nsTableColFrame::GetRightBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mRightBorderWidth) : mRightBorderWidth; + return width; +} + +inline void nsTableColFrame::SetRightBorderWidth(nscoord aWidth) +{ + mRightBorderWidth = aWidth; +} #endif diff --git a/mozilla/layout/tables/nsTableColGroupFrame.cpp b/mozilla/layout/tables/nsTableColGroupFrame.cpp index 15c7dab5216..41a96c8d000 100644 --- a/mozilla/layout/tables/nsTableColGroupFrame.cpp +++ b/mozilla/layout/tables/nsTableColGroupFrame.cpp @@ -250,29 +250,6 @@ nsTableColGroupFrame::SetInitialChildList(nsIPresContext* aPresContext, return NS_OK; } - nsIFrame* kidFrame = aChildList; - while (kidFrame) { - nsIAtom* kidType; - kidFrame->GetFrameType(&kidType); - if (nsLayoutAtoms::tableColFrame == kidType) { - // Set the preliminary values for the column frame - PRInt32 span = ((nsTableColFrame*)kidFrame)->GetSpan(); - if (span > 1) { - nsTableColFrame* firstSpannedCol; - tableFrame->CreateAnonymousColFrames(*aPresContext, *this, span - 1, eColAnonymousCol, - PR_FALSE, (nsTableColFrame*)kidFrame, (nsIFrame **)&firstSpannedCol); - nsIFrame* spanner = kidFrame; - kidFrame->GetNextSibling(&kidFrame); // need to do this before we insert the new frames - nsFrameList newChildren(aChildList); // used as a convience to hook up siblings - newChildren.InsertFrames(this, (nsTableColFrame*)spanner, (nsIFrame *)firstSpannedCol); - NS_RELEASE(kidType); - continue; - } - } - NS_IF_RELEASE(kidType); - kidFrame->GetNextSibling(&kidFrame); - } - mFrames.AppendFrames(this, aChildList); return NS_OK; } diff --git a/mozilla/layout/tables/nsTableFrame.cpp b/mozilla/layout/tables/nsTableFrame.cpp index 7a2d5dc94d6..718136af383 100644 --- a/mozilla/layout/tables/nsTableFrame.cpp +++ b/mozilla/layout/tables/nsTableFrame.cpp @@ -38,7 +38,6 @@ #include "nsCOMPtr.h" #include "nsVoidArray.h" #include "nsTableFrame.h" -#include "nsTableBorderCollapser.h" #include "nsIRenderingContext.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" @@ -75,6 +74,7 @@ #include "nsIScrollableFrame.h" #include "nsHTMLReflowCommand.h" #include "nsIFrameManager.h" +#include "nsStyleUtil.h" // helper function for dealing with placeholder for positioned/floated table static void GetPlaceholderFor(nsIPresContext& aPresContext, nsIFrame& aFrame, nsIFrame** aPlaceholder); @@ -108,17 +108,19 @@ struct nsTableReflowState { // The first body section row group frame, i.e. not a header or footer nsIFrame* firstBodySection; - nsTableReflowState(const nsHTMLReflowState& aReflowState, + nsTableReflowState(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nsTableFrame& aTableFrame, nsReflowReason aReason, nscoord aAvailWidth, nscoord aAvailHeight) : reflowState(aReflowState) { - Init(aTableFrame, aReason, aAvailWidth, aAvailHeight); + Init(aPresContext, aTableFrame, aReason, aAvailWidth, aAvailHeight); } - void Init(nsTableFrame& aTableFrame, + void Init(nsIPresContext& aPresContext, + nsTableFrame& aTableFrame, nsReflowReason aReason, nscoord aAvailWidth, nscoord aAvailHeight) @@ -126,7 +128,7 @@ struct nsTableReflowState { reason = aReason; nsTableFrame* table = (nsTableFrame*)aTableFrame.GetFirstInFlow(); - nsMargin borderPadding = table->GetBorderPadding(reflowState); + nsMargin borderPadding = table->GetChildAreaOffset(aPresContext, &reflowState); x = borderPadding.left; y = borderPadding.top; @@ -145,23 +147,16 @@ struct nsTableReflowState { firstBodySection = nsnull; } - nsTableReflowState(const nsHTMLReflowState& aReflowState, + nsTableReflowState(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, nsTableFrame& aTableFrame) : reflowState(aReflowState) { - Init(aTableFrame, aReflowState.reason, aReflowState.availableWidth, aReflowState.availableHeight); + Init(aPresContext, aTableFrame, aReflowState.reason, aReflowState.availableWidth, aReflowState.availableHeight); } }; -nscoord -GetHorBorderPaddingWidth(const nsHTMLReflowState& aReflowState, - nsTableFrame* aTableFrame) -{ - nsMargin borderPadding = aTableFrame->GetBorderPadding(aReflowState); - return borderPadding.left + borderPadding.right; -} - /******************************************************************************** ** nsTableFrame ** ********************************************************************************/ @@ -169,6 +164,18 @@ GetHorBorderPaddingWidth(const nsHTMLReflowState& aReflowState, static PRInt32 gRflCount = 0; #endif +struct BCPropertyData +{ + BCPropertyData() { mDamageArea.x = mDamageArea.y = mDamageArea.width = mDamageArea.height = + mTopBorderWidth = mRightBorderWidth = mBottomBorderWidth = mLeftBorderWidth = 0; } + nsRect mDamageArea; + PRUint8 mTopBorderWidth; + PRUint8 mRightBorderWidth; + PRUint8 mBottomBorderWidth; + PRUint8 mLeftBorderWidth; +}; + + NS_IMETHODIMP nsTableFrame::GetFrameType(nsIAtom** aType) const { @@ -193,6 +200,8 @@ nsTableFrame::nsTableFrame() mBits.mNeedStrategyBalance = PR_TRUE; mBits.mCellSpansPctCol = PR_FALSE; mBits.mRequestedTimeoutReflow = PR_FALSE; + mBits.mNeedToCalcBCBorders = PR_FALSE; + mBits.mIsBorderCollapse = PR_FALSE; #ifdef DEBUG_TABLE_REFLOW_TIMING mTimer = new nsReflowTimer(this); @@ -236,11 +245,6 @@ nsTableFrame::Init(nsIPresContext* aPresContext, { nsresult rv; - // Create the cell map - // XXX Why do we do this for continuing frames? - mCellMap = new nsTableCellMap(aPresContext, *this); - if (!mCellMap) return NS_ERROR_OUT_OF_MEMORY; - // Let the base class do its processing rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow); @@ -248,6 +252,16 @@ nsTableFrame::Init(nsIPresContext* aPresContext, // record that children that are ignorable whitespace should be excluded mState |= NS_FRAME_EXCLUDE_IGNORABLE_WHITESPACE; + // see if border collapse is on, if so set it + const nsStyleTableBorder* tableStyle = + (const nsStyleTableBorder*)mStyleContext->GetStyleData(eStyleStruct_TableBorder); + PRBool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); + SetBorderCollapse(borderCollapse); + // Create the cell map + // XXX Why do we do this for continuing frames? + mCellMap = new nsTableCellMap(aPresContext, *this, borderCollapse); + if (!mCellMap) return NS_ERROR_OUT_OF_MEMORY; + if (aPrevInFlow) { // set my width, because all frames in a table flow are the same width and // code in nsTableOuterFrame depends on this being set @@ -477,10 +491,11 @@ nsTableFrame::SetInitialChildList(nsIPresContext* aPresContext, // anonymous ones due to cells in rows. InsertColGroups(*aPresContext, 0, mColGroups.FirstChild()); AppendRowGroups(*aPresContext, mFrames.FirstChild()); - } - - if (HasGroupRules()) { - ProcessGroupRules(aPresContext); + // calc collapsing borders if this is the default (row group, col group, child list) + if (!aChildList && IsBorderCollapse()) { + nsRect damageArea(0, 0, GetColCount(), GetRowCount()); + SetBCDamageArea(*aPresContext, damageArea); + } } return rv; @@ -500,7 +515,7 @@ void nsTableFrame::AttributeChangedFor(nsIPresContext* aPresContext, { nsIAtom* frameType; aFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableCellFrame == frameType) { + if (IS_TABLE_CELL(frameType)) { if ((nsHTMLAtoms::rowspan == aAttribute) || (nsHTMLAtoms::colspan == aAttribute)) { nsTableCellMap* cellMap = GetCellMap(); @@ -550,7 +565,7 @@ PRInt32 nsTableFrame::GetRowCount () const } /* return the col count including dead cols */ -PRInt32 nsTableFrame::GetColCount () +PRInt32 nsTableFrame::GetColCount () const { PRInt32 colCount = 0; nsTableCellMap* cellMap = GetCellMap(); @@ -562,7 +577,7 @@ PRInt32 nsTableFrame::GetColCount () } /* return the effective col count */ -PRInt32 nsTableFrame::GetEffectiveColCount () +PRInt32 nsTableFrame::GetEffectiveColCount() const { PRInt32 colCount = GetColCount(); // don't count cols at the end that don't have originating cells @@ -659,66 +674,6 @@ PRInt32 nsTableFrame::GetEffectiveCOLSAttribute() return result; } -PRBool nsTableFrame::HasGroupRules() const -{ - const nsStyleTable* tableStyle = nsnull; - GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle); - if (NS_STYLE_TABLE_RULES_GROUPS == tableStyle->mRules) { - return PR_TRUE; - } - return PR_FALSE; -} - -// this won't work until bug 12948 is resolved and col groups are considered -void nsTableFrame::ProcessGroupRules(nsIPresContext* aPresContext) -{ -#if 0 - // The RULES code below has been disabled because collapsing borders have been disabled - // and RULES depend on collapsing borders - PRInt32 numCols = GetColCount(); - - // process row groups - nsIFrame* iFrame; - for (iFrame = mFrames.FirstChild(); iFrame; iFrame->GetNextSibling(&iFrame)) { - nsIAtom* frameType; - iFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableRowGroupFrame == frameType) { - nsTableRowGroupFrame* rgFrame = (nsTableRowGroupFrame *)iFrame; - PRInt32 startRow = rgFrame->GetStartRowIndex(); - PRInt32 numGroupRows = rgFrame->GetRowCount(); - PRInt32 endRow = startRow + numGroupRows - 1; - if (startRow == endRow) { - continue; - } - for (PRInt32 rowX = startRow; rowX <= endRow; rowX++) { - for (PRInt32 colX = 0; colX < numCols; colX++) { - PRBool originates; - nsTableCellFrame* cell = GetCellInfoAt(rowX, colX, &originates); - if (originates) { - nsCOMPtr styleContext; - cell->GetStyleContext(getter_AddRefs(styleContext)); - nsStyleBorder* border = (nsStyleBorder*)styleContext->GetMutableStyleData(eStyleStruct_Border); - if (rowX == startRow) { - border->SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - } - else if (rowX == endRow) { - border->SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - } - else { - border->SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE); - border->SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE); - } - styleContext->RecalcAutomaticData(aPresContext); - } - } - } - } - NS_IF_RELEASE(frameType); - } -#endif -} - - void nsTableFrame::AdjustRowIndices(nsIPresContext* aPresContext, PRInt32 aRowIndex, PRInt32 aAdjustment) @@ -833,6 +788,11 @@ void nsTableFrame::InsertCol(nsIPresContext& aPresContext, } } } + // for now, just bail and recalc all of the collapsing borders + if (IsBorderCollapse()) { + nsRect damageArea(0, 0, PR_MAX(1, GetColCount()), PR_MAX(1, GetRowCount())); + SetBCDamageArea(aPresContext, damageArea); + } } void nsTableFrame::RemoveCol(nsIPresContext& aPresContext, @@ -850,6 +810,11 @@ void nsTableFrame::RemoveCol(nsIPresContext& aPresContext, CreateAnonymousColFrames(aPresContext, 1, eColAnonymousCell, PR_TRUE); } } + // for now, just bail and recalc all of the collapsing borders + if (IsBorderCollapse()) { + nsRect damageArea(0, 0, GetColCount(), GetRowCount()); + SetBCDamageArea(aPresContext, damageArea); + } } /** Get the cell map for this table frame. It is not always mCellMap. @@ -1075,14 +1040,16 @@ nsTableFrame::CreateAnonymousColFrames(nsIPresContext& aPresContext, } } -PRInt32 nsTableFrame::AppendCell(nsIPresContext& aPresContext, - nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex) +void +nsTableFrame::AppendCell(nsIPresContext& aPresContext, + nsTableCellFrame& aCellFrame, + PRInt32 aRowIndex) { PRInt32 colIndex = 0; nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - colIndex = cellMap->AppendCell(aCellFrame, aRowIndex, PR_TRUE); + nsRect damageArea(0,0,0,0); + cellMap->AppendCell(aCellFrame, aRowIndex, PR_TRUE, damageArea); PRInt32 numColsInMap = GetColCount(); PRInt32 numColsInCache = mColFrames.Count(); PRInt32 numColsToAdd = numColsInMap - numColsInCache; @@ -1090,8 +1057,10 @@ PRInt32 nsTableFrame::AppendCell(nsIPresContext& aPresContext, // this sets the child list, updates the col cache and cell map CreateAnonymousColFrames(aPresContext, numColsToAdd, eColAnonymousCell, PR_TRUE); } + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } - return colIndex; } void nsTableFrame::InsertCells(nsIPresContext& aPresContext, @@ -1101,7 +1070,8 @@ void nsTableFrame::InsertCells(nsIPresContext& aPresContext, { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore); + nsRect damageArea(0,0,0,0); + cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea); PRInt32 numColsInMap = GetColCount(); PRInt32 numColsInCache = mColFrames.Count(); PRInt32 numColsToAdd = numColsInMap - numColsInCache; @@ -1109,6 +1079,9 @@ void nsTableFrame::InsertCells(nsIPresContext& aPresContext, // this sets the child list, updates the col cache and cell map CreateAnonymousColFrames(aPresContext, numColsToAdd, eColAnonymousCell, PR_TRUE); } + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } } @@ -1145,7 +1118,8 @@ void nsTableFrame::RemoveCell(nsIPresContext& aPresContext, { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - cellMap->RemoveCell(aCellFrame, aRowIndex); + nsRect damageArea(0,0,0,0); + cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea); PRInt32 numColsInMap = GetColCount(); // cell map's notion of num cols PRInt32 numColsInCache = mColFrames.Count(); if (numColsInCache > numColsInMap) { @@ -1156,6 +1130,10 @@ void nsTableFrame::RemoveCell(nsIPresContext& aPresContext, } } else NS_ASSERTION(numColsInCache == numColsInMap, "cell map has too many cols"); + + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } } @@ -1217,9 +1195,10 @@ nsTableFrame::InsertRows(nsIPresContext& aPresContext, PRInt32 numColsToAdd = 0; nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { + nsRect damageArea(0,0,0,0); PRInt32 origNumRows = cellMap->GetRowCount(); PRInt32 numNewRows = aRowFrames.Count(); - cellMap->InsertRows(&aPresContext, aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans); + cellMap->InsertRows(&aPresContext, aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea); PRInt32 numColsInMap = GetColCount(); // cell map's notion of num cols PRInt32 numColsInCache = mColFrames.Count(); numColsToAdd = numColsInMap - numColsInCache; @@ -1237,6 +1216,9 @@ nsTableFrame::InsertRows(nsIPresContext& aPresContext, nsTableRowFrame* rowFrame = (nsTableRowFrame *) aRowFrames.ElementAt(rowX); rowFrame->SetRowIndex(aRowIndex + rowX); } + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } //printf("insertRowsAfter \n"); @@ -1262,7 +1244,7 @@ void nsTableFrame::RemoveRows(nsIPresContext& aPresContext, for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking); kidFrame = kidFrame->GetNextSibling()) { nsCOMPtr kidType; kidFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == kidType.get()) { + if (IS_TABLE_CELL(kidType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)kidFrame; stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(), cellFrame->GetMaximumWidth(), PR_TRUE); @@ -1275,7 +1257,8 @@ void nsTableFrame::RemoveRows(nsIPresContext& aPresContext, PRInt32 firstRowIndex = aFirstRowFrame.GetRowIndex(); nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - cellMap->RemoveRows(&aPresContext, firstRowIndex, aNumRowsToRemove, aConsiderSpans); + nsRect damageArea(0,0,0,0); + cellMap->RemoveRows(&aPresContext, firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea); // only remove cols that are of type eTypeAnonymous cell (they are at the end) PRInt32 numColsInMap = GetColCount(); // cell map's notion of num cols PRInt32 numColsInCache = mColFrames.Count(); @@ -1287,6 +1270,10 @@ void nsTableFrame::RemoveRows(nsIPresContext& aPresContext, } } else NS_ASSERTION(numColsInCache == numColsInMap, "cell map has too many cols"); + + if (IsBorderCollapse()) { + SetBCDamageArea(aPresContext, damageArea); + } } AdjustRowIndices(&aPresContext, firstRowIndex, -aNumRowsToRemove); //printf("removeRowsAfter\n"); @@ -1307,7 +1294,7 @@ void nsTableFrame::AppendRowGroups(nsIPresContext& aPresContext, nsTableRowGroupFrame* nsTableFrame::GetRowGroupFrame(nsIFrame* aFrame, - nsIAtom* aFrameTypeIn) + nsIAtom* aFrameTypeIn) const { nsIFrame* rgFrame = nsnull; nsIAtom* frameType = aFrameTypeIn; @@ -1479,12 +1466,12 @@ nsTableFrame::Paint(nsIPresContext* aPresContext, } PRIntn skipSides = GetSkipSides(); - if (NS_STYLE_BORDER_SEPARATE == GetBorderCollapseStyle()) { - nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, - aDirtyRect, rect, *border, mStyleContext, skipSides); + if (IsBorderCollapse()) { + PaintBCBorders(aPresContext, aRenderingContext, aDirtyRect); } else { - // tbd + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *border, mStyleContext, skipSides); } } } @@ -1703,6 +1690,7 @@ nsTableFrame::SetColumnDimensions(nsIPresContext* aPresContext, nscoord aHeight, const nsMargin& aBorderPadding) { + if (!aPresContext) ABORT0(); nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom; nscoord cellSpacingX = GetCellSpacingX(); @@ -1831,7 +1819,7 @@ nsTableFrame::NotifyAncestorsOfSpecialReflow(nsIFrame& aFrame) for (aFrame.GetParent(&parent); parent; parent->GetParent(&parent)) { nsCOMPtr frameType; parent->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { ((nsTableCellFrame*)parent)->SetNeedSpecialReflow(PR_TRUE); } else if (nsLayoutAtoms::tableRowFrame == frameType.get()) { @@ -1901,8 +1889,13 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); - - PRBool doCollapse = PR_FALSE; + + // see if collapsing borders need to be calculated + if (!mPrevInFlow && IsBorderCollapse() && NeedToCalcBCBorders()) { + GET_TWIPS_TO_PIXELS(aPresContext, p2t); + CalcBCBorders(*aPresContext); + } + PRBool doCollapse = PR_FALSE; // collapsing rows, cols, etc. aDesiredSize.width = aReflowState.availableWidth; @@ -1926,7 +1919,7 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, // only do pass1 reflow on an auto layout table nsReflowReason reason = (eReflowReason_Initial == aReflowState.reason) ? eReflowReason_Initial : eReflowReason_StyleChange; - nsTableReflowState reflowState(aReflowState, *this, reason, + nsTableReflowState reflowState(*aPresContext, aReflowState, *this, reason, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); // reflow the children ReflowChildren(aPresContext, reflowState, !HaveReflowedColGroups(), PR_FALSE, aStatus); @@ -1982,7 +1975,7 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, } // see if an extra reflow will be necessary in pagination mode when there is a specified table height else if (isPaginated && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aPresContext, aReflowState); if ((tableSpecifiedHeight > 0) && (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { SetNeedToInitiateSpecialReflow(PR_TRUE); @@ -2047,7 +2040,8 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext, } #endif - SetColumnDimensions(aPresContext, aDesiredSize.height, aReflowState.mComputedBorderPadding); + nsMargin borderPadding = GetChildAreaOffset(*aPresContext, &aReflowState); + SetColumnDimensions(aPresContext, aDesiredSize.height, borderPadding); if (doCollapse) { AdjustForCollapsingRows(aPresContext, aDesiredSize.height); AdjustForCollapsingCols(aPresContext, aDesiredSize.width); @@ -2111,7 +2105,7 @@ nsTableFrame::ReflowTable(nsIPresContext* aPresContext, // Constrain our reflow width to the computed table width (of the 1st in flow). // and our reflow height to our avail height minus border, padding, cellspacing aDesiredSize.width = GetDesiredWidth(); - nsTableReflowState reflowState(aReflowState, *this, aReason, + nsTableReflowState reflowState(*aPresContext, aReflowState, *this, aReason, aDesiredSize.width, aAvailHeight); ReflowChildren(aPresContext, reflowState, haveReflowedColGroups, PR_FALSE, aStatus); @@ -2292,7 +2286,7 @@ nsTableFrame::CollapseRowGroupIfNecessary(nsIPresContext* aPresContext, PRInt32 numCols = cellMap->GetColCount(); nsTableCellFrame* lastCell = nsnull; for (int colX = 0; colX < numCols; colX++) { - CellData* cellData = cellMap->GetCellAt(aRowX, colX); + CellData* cellData = cellMap->GetDataAt(aRowX, colX); if (cellData && cellData->IsSpan()) { // a cell above is spanning into here // adjust the real cell's rect only once nsTableCellFrame* realCell = nsnull; @@ -2389,7 +2383,7 @@ NS_METHOD nsTableFrame::AdjustForCollapsingCols(nsIPresContext* aPresContext, nsTableCellFrame* lastCell = nsnull; nsTableCellFrame* cellFrame = nsnull; for (PRInt32 rowX = 0; rowX < numRows; rowX++) { - CellData* cellData = cellMap->GetCellAt(rowX, colX); + CellData* cellData = cellMap->GetDataAt(rowX, colX); nsRect cellRect; if (cellData) { if (cellData->IsOrig()) { // the cell originates at (rowX, colX) @@ -2670,7 +2664,7 @@ nsTableFrame::IncrementalReflow(nsIPresContext* aPresContext, nsTableFrame* table = (nsTableFrame*)GetFirstInFlow(); lastWidth = table->mRect.width; } - nsTableReflowState state(aReflowState, *this, eReflowReason_Incremental, + nsTableReflowState state(*aPresContext, aReflowState, *this, eReflowReason_Incremental, lastWidth, aReflowState.availableHeight); // determine if this frame is the target or not @@ -2721,7 +2715,7 @@ nsTableFrame::IR_TargetIsMe(nsIPresContext* aPresContext, break; case eReflowType_ReflowDirty: { // reflow the dirty children - nsTableReflowState reflowState(aReflowState.reflowState, *this, eReflowReason_Initial, + nsTableReflowState reflowState(*aPresContext, aReflowState.reflowState, *this, eReflowReason_Initial, aReflowState.availSize.width, aReflowState.availSize.height); PRBool reflowedAtLeastOne; ReflowChildren(aPresContext, reflowState, PR_FALSE, PR_TRUE, aStatus, &reflowedAtLeastOne); @@ -2753,25 +2747,127 @@ NS_METHOD nsTableFrame::IR_StyleChanged(nsIPresContext* aPresContext, return NS_OK; } -nsMargin -nsTableFrame::GetBorderPadding(const nsHTMLReflowState& aReflowState) const +static void +DivideBCBorderSize(nscoord aPixelSize, + nscoord& aSmallHalf, + nscoord& aLargeHalf) +{ + aSmallHalf = aPixelSize / 2; + aLargeHalf = ((aSmallHalf + aSmallHalf) < aPixelSize) ? aSmallHalf + 1 : aSmallHalf; +} + +nsMargin* +nsTableFrame::GetBCBorder(nsIPresContext& aPresContext, + PRBool aInnerBorderOnly, + nsMargin& aBorder) const +{ + aBorder.top = aBorder.right = aBorder.bottom = aBorder.left = 0; + + GET_PIXELS_TO_TWIPS(&aPresContext, p2t); + BCPropertyData* propData = + (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, (nsIFrame*)this, nsLayoutAtoms::tableBCProperty, PR_FALSE); + if (propData) { + nsCompatibility mode; + aPresContext.GetCompatibilityMode(&mode); + if ((eCompatibility_Standard == mode) || aInnerBorderOnly) { + nscoord smallHalf, largeHalf; + + DivideBCBorderSize(propData->mTopBorderWidth, smallHalf, largeHalf); + aBorder.top += NSToCoordRound(p2t * (float)smallHalf); + + DivideBCBorderSize(propData->mRightBorderWidth, smallHalf, largeHalf); + aBorder.right += NSToCoordRound(p2t * (float)largeHalf); + + DivideBCBorderSize(propData->mBottomBorderWidth, smallHalf, largeHalf); + aBorder.bottom += NSToCoordRound(p2t * (float)largeHalf); + + DivideBCBorderSize(propData->mLeftBorderWidth, smallHalf, largeHalf); + aBorder.left += NSToCoordRound(p2t * (float)smallHalf); + } + else { + aBorder.top += NSToCoordRound(p2t * (float)propData->mTopBorderWidth); + aBorder.right += NSToCoordRound(p2t * (float)propData->mRightBorderWidth); + aBorder.bottom += NSToCoordRound(p2t * (float)propData->mBottomBorderWidth); + aBorder.left += NSToCoordRound(p2t * (float)propData->mLeftBorderWidth); + } + } + return &aBorder; +} + +static +void GetSeparateModelBorderPadding(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState, + nsIStyleContext& aStyleContext, + nsMargin& aBorderPadding) { const nsStyleBorder* border = - (const nsStyleBorder*)mStyleContext->GetStyleData(eStyleStruct_Border); - nsMargin borderPadding; - border->GetBorder(borderPadding); - borderPadding += aReflowState.mComputedPadding; - return borderPadding; + (const nsStyleBorder*)aStyleContext.GetStyleData(eStyleStruct_Border); + border->GetBorder(aBorderPadding); + if (aReflowState) { + aBorderPadding += aReflowState->mComputedPadding; + } +} + +nsMargin +nsTableFrame::GetChildAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const +{ + nsMargin offset(0,0,0,0); + if (IsBorderCollapse()) { + nsCompatibility mode; + aPresContext.GetCompatibilityMode(&mode); + if (eCompatibility_NavQuirks == mode) { + nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT1(offset); + nscoord smallHalf, largeHalf; + GET_PIXELS_TO_TWIPS(&aPresContext, p2t); + BCPropertyData* propData = + (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, (nsIFrame*)firstInFlow, nsLayoutAtoms::tableBCProperty, PR_FALSE); + if (!propData) ABORT1(offset); + + DivideBCBorderSize(propData->mTopBorderWidth, smallHalf, largeHalf); + offset.top += NSToCoordRound(p2t * (float)largeHalf); + + DivideBCBorderSize(propData->mRightBorderWidth, smallHalf, largeHalf); + offset.right += NSToCoordRound(p2t * (float)smallHalf); + + DivideBCBorderSize(propData->mBottomBorderWidth, smallHalf, largeHalf); + offset.bottom += NSToCoordRound(p2t * (float)smallHalf); + + DivideBCBorderSize(propData->mLeftBorderWidth, smallHalf, largeHalf); + offset.left += NSToCoordRound(p2t * (float)largeHalf); + } + } + else { + if (!mStyleContext) ABORT1(offset); + GetSeparateModelBorderPadding(aPresContext, aReflowState, *mStyleContext, offset); + } + return offset; +} + +nsMargin +nsTableFrame::GetContentAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const +{ + nsMargin offset(0,0,0,0); + if (IsBorderCollapse()) { + GetBCBorder(aPresContext, PR_FALSE, offset); + } + else { + if (!mStyleContext) ABORT1(offset); + GetSeparateModelBorderPadding(aPresContext, aReflowState, *mStyleContext, offset); + } + return offset; } // Recovers the reflow state to what it should be if aKidFrame is about to be // reflowed. Restores y, footerFrame, firstBodySection and availSize.height (if // the height is constrained) nsresult -nsTableFrame::RecoverState(nsTableReflowState& aReflowState, +nsTableFrame::RecoverState(nsIPresContext& aPresContext, + nsTableReflowState& aReflowState, nsIFrame* aKidFrame) { - nsMargin borderPadding = GetBorderPadding(aReflowState.reflowState); + nsMargin borderPadding = GetChildAreaOffset(aPresContext, &aReflowState.reflowState); aReflowState.y = borderPadding.top; nscoord cellSpacingY = GetCellSpacingY(); @@ -2827,6 +2923,23 @@ nsTableFrame::RecoverState(nsTableReflowState& aReflowState, return NS_OK; } +void +nsTableFrame::InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState) +{ + nsMargin collapseBorder; + nsMargin padding(0,0,0,0); + nsMargin* pCollapseBorder = nsnull; + if (IsBorderCollapse()) { + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aReflowState.frame); + if (rgFrame) { + GET_PIXELS_TO_TWIPS(&aPresContext, p2t); + pCollapseBorder = rgFrame->GetBCBorderWidth(p2t, collapseBorder); + } + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); +} + NS_METHOD nsTableFrame::IR_TargetIsChild(nsIPresContext* aPresContext, nsTableReflowState& aReflowState, @@ -2834,9 +2947,10 @@ nsTableFrame::IR_TargetIsChild(nsIPresContext* aPresContext, nsIFrame* aNextFrame) { + if (!aPresContext) ABORT1(NS_ERROR_NULL_POINTER); nsresult rv; // Recover the state as if aNextFrame is about to be reflowed - RecoverState(aReflowState, aNextFrame); + RecoverState(*aPresContext, aReflowState, aNextFrame); // Remember the old rect nsRect oldKidRect; @@ -2844,8 +2958,10 @@ nsTableFrame::IR_TargetIsChild(nsIPresContext* aPresContext, // Pass along the reflow command, don't request a max element size, rows will do that nsHTMLReflowMetrics desiredSize(nsnull); - nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, - aNextFrame, aReflowState.availSize); + nsSize kidAvailSize(aReflowState.availSize); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, aNextFrame, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, kidReflowState); rv = ReflowChild(aNextFrame, aPresContext, desiredSize, kidReflowState, aReflowState.x, aReflowState.y, 0, aStatus); @@ -2969,7 +3085,7 @@ nsTableFrame::OrderRowGroups(nsVoidArray& aChildren, PRUint32& aNumRowGroups, nsIFrame** aFirstBody, nsTableRowGroupFrame** aHead, - nsTableRowGroupFrame** aFoot) + nsTableRowGroupFrame** aFoot) const { aChildren.Clear(); nsIFrame* head = nsnull; @@ -3069,6 +3185,10 @@ nsTableFrame::ReflowChildren(nsIPresContext* aPresContext, PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); + nsMargin collapseBorder; + nsMargin padding(0,0,0,0); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + nsAutoVoidArray rowGroups; PRUint32 numRowGroups; nsTableRowGroupFrame *thead, *tfoot; @@ -3117,8 +3237,9 @@ nsTableFrame::ReflowChildren(nsIPresContext* aPresContext, if (childX < numRowGroups) { // Reflow the child into the available space - nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, - kidFrame, kidAvailSize, aReflowState.reason); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, kidFrame, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, kidReflowState); // XXX fix up bad mComputedWidth for scroll frame kidReflowState.mComputedWidth = PR_MAX(kidReflowState.mComputedWidth, 0); @@ -3172,8 +3293,9 @@ nsTableFrame::ReflowChildren(nsIPresContext* aPresContext, } if (repeatedFooter) { kidAvailSize.height = repeatedFooterHeight; - nsHTMLReflowState footerReflowState(aPresContext, aReflowState.reflowState, - repeatedFooter, kidAvailSize, aReflowState.reason); + nsHTMLReflowState footerReflowState(aPresContext, aReflowState.reflowState, repeatedFooter, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, footerReflowState); aReflowState.y += cellSpacingY; nsReflowStatus footerStatus; rv = ReflowChild(repeatedFooter, aPresContext, desiredSize, footerReflowState, @@ -3268,7 +3390,7 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, nscoord minWidth, prefWidth; CalcMinAndPreferredWidths(aPresContext, aReflowState, PR_FALSE, minWidth, prefWidth); SetMinWidth(minWidth); - nscoord desWidth = CalcDesiredWidth(aReflowState); + nscoord desWidth = CalcDesiredWidth(*aPresContext, aReflowState); SetDesiredWidth(desWidth); SetPreferredWidth(prefWidth); @@ -3285,7 +3407,8 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, // This width is based on the column widths array of the table. // sum the width of each column and add in table insets nscoord -nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState) +nsTableFrame::CalcDesiredWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState) { NS_ASSERTION(!mPrevInFlow, "never ever call me on a continuing frame!"); nsTableCellMap* cellMap = GetCellMap(); @@ -3308,8 +3431,9 @@ nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState) if (numCols > 0) { tableWidth += cellSpacing; // add last cellspacing - // Compute the insets (sum of border and padding) - tableWidth += GetHorBorderPaddingWidth(aReflowState, this); + // Add the width between the border edge and the child area + nsMargin childOffset = GetChildAreaOffset(aPresContext, &aReflowState); + tableWidth += childOffset.left + childOffset.right; } return tableWidth; @@ -3326,7 +3450,7 @@ nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, return 0; } nscoord cellSpacingY = GetCellSpacingY(); - nsMargin borderPadding = GetBorderPadding(aReflowState); + nsMargin borderPadding = GetChildAreaOffset(*aPresContext, &aReflowState); // get the natural height based on the last child's (row group or scroll frame) rect nsAutoVoidArray rowGroups; @@ -3346,7 +3470,7 @@ nsTableFrame::CalcDesiredHeight(nsIPresContext* aPresContext, // see if a specified table height requires dividing additional space to rows if (!mPrevInFlow) { - nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); + nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aPresContext, aReflowState); if ((tableSpecifiedHeight > 0) && (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && (tableSpecifiedHeight > desiredHeight)) { @@ -3391,7 +3515,9 @@ nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, nscoord cellSpacingY = GetCellSpacingY(); nscoord sumOfRowHeights = 0; - nscoord rowGroupYPos = aReflowState.mComputedBorderPadding.top + cellSpacingY; + nsMargin borderPadding; + GetChildAreaOffset(*aPresContext, &aReflowState); + nscoord rowGroupYPos = borderPadding.top + cellSpacingY; nsVoidArray rowGroups; PRUint32 numRowGroups; @@ -3402,7 +3528,7 @@ nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, // height, and base the pct on the table height. If the row group had a computed // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights nscoord pctBasis = aReflowState.mComputedHeight - (GetCellSpacingY() * (GetRowCount() + 1)); - nscoord yOriginRG = aReflowState.mComputedBorderPadding.top + GetCellSpacingY(); + nscoord yOriginRG = borderPadding.top + GetCellSpacingY(); nscoord yEndRG = yOriginRG; PRUint32 rgX; for (rgX = 0; (rgX < numRowGroups); rgX++) { @@ -3503,7 +3629,7 @@ nsTableFrame::DistributeHeightToRows(nsIPresContext* aPresContext, // allocate the extra height to the unstyled row groups and rows pctBasis = aAmount - amountUsed; - yOriginRG = aReflowState.mComputedBorderPadding.top + cellSpacingY; + yOriginRG = borderPadding.top + cellSpacingY; yEndRG = yOriginRG; for (rgX = 0; rgX < numRowGroups; rgX++) { nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX)); @@ -3817,61 +3943,6 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth) } } -// Update the border style to map to the HTML border style -void nsTableFrame::MapHTMLBorderStyle(nsStyleBorder& aBorderStyle, nscoord aBorderWidth) -{ - nsStyleCoord width; - width.SetCoordValue(aBorderWidth); - aBorderStyle.mBorder.SetTop(width); - aBorderStyle.mBorder.SetLeft(width); - aBorderStyle.mBorder.SetBottom(width); - aBorderStyle.mBorder.SetRight(width); - - aBorderStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_BG_OUTSET); - aBorderStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_BG_OUTSET); - aBorderStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_BG_OUTSET); - aBorderStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_BG_OUTSET); - - nsIStyleContext* styleContext = mStyleContext; - const nsStyleBackground* colorData = (const nsStyleBackground*) - styleContext->GetStyleData(eStyleStruct_Background); - - // Look until we find a style context with a NON-transparent background color - while (styleContext) { - if ((colorData->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) != 0) { - nsIStyleContext* temp = styleContext; - styleContext = styleContext->GetParent(); - if (temp != mStyleContext) - NS_RELEASE(temp); - colorData = (const nsStyleBackground*)styleContext->GetStyleData(eStyleStruct_Background); - } - else { - break; - } - } - - // Yaahoo, we found a style context which has a background color - - nscolor borderColor = 0xFFC0C0C0; - - if (styleContext) { - borderColor = colorData->mBackgroundColor; - if (styleContext != mStyleContext) - NS_RELEASE(styleContext); - } - - // if the border color is white, then shift to grey - if (borderColor == 0xFFFFFFFF) - borderColor = 0xFFC0C0C0; - - aBorderStyle.SetBorderColor(NS_SIDE_TOP, borderColor); - aBorderStyle.SetBorderColor(NS_SIDE_LEFT, borderColor); - aBorderStyle.SetBorderColor(NS_SIDE_BOTTOM, borderColor); - aBorderStyle.SetBorderColor(NS_SIDE_RIGHT, borderColor); - -} - - PRBool nsTableFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult) { @@ -3887,10 +3958,6 @@ PRBool nsTableFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, return PR_TRUE; } -void nsTableFrame::MapBorderMarginPadding(nsIPresContext* aPresContext) -{ -} - nscoord CalcPercentPadding(nscoord aBasis, nsStyleCoord aStyleCoord) @@ -3977,24 +4044,13 @@ nsTableFrame::GetPadding(const nsSize& aBasis, return padding; } -PRUint8 nsTableFrame::GetBorderCollapseStyle() -{ - /* the following has been commented out to turn off collapsing borders - const nsStyleTable* tableStyle; - GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle); - return tableStyle->mBorderCollapse;*/ - return NS_STYLE_BORDER_SEPARATE; -} - - // XXX: could cache this. But be sure to check style changes if you do! nscoord nsTableFrame::GetCellSpacingX() { - const nsStyleTableBorder* tableStyle; - GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); nscoord cellSpacing = 0; - PRUint8 borderCollapseStyle = GetBorderCollapseStyle(); - if (NS_STYLE_BORDER_COLLAPSE != borderCollapseStyle) { + if (!IsBorderCollapse()) { + const nsStyleTableBorder* tableStyle; + GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); if (tableStyle->mBorderSpacingX.GetUnit() == eStyleUnit_Coord) { cellSpacing = tableStyle->mBorderSpacingX.GetCoordValue(); } @@ -4005,11 +4061,10 @@ nscoord nsTableFrame::GetCellSpacingX() // XXX: could cache this. But be sure to check style changes if you do! nscoord nsTableFrame::GetCellSpacingY() { - const nsStyleTableBorder* tableStyle; - GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); nscoord cellSpacing = 0; - PRUint8 borderCollapseStyle = GetBorderCollapseStyle(); - if (NS_STYLE_BORDER_COLLAPSE != borderCollapseStyle) { + if (!IsBorderCollapse()) { + const nsStyleTableBorder* tableStyle; + GetStyleData(eStyleStruct_TableBorder, (const nsStyleStruct *&)tableStyle); if (tableStyle->mBorderSpacingY.GetUnit() == eStyleUnit_Coord) { cellSpacing = tableStyle->mBorderSpacingY.GetCoordValue(); } @@ -4113,7 +4168,7 @@ nsTableFrame::CalcBorderBoxWidth(nsIPresContext* aPresContext, } } else if (width != NS_UNCONSTRAINEDSIZE) { - nsMargin borderPadding = aState.mComputedBorderPadding; + nsMargin borderPadding = GetContentAreaOffset(*aPresContext, &aState); width += borderPadding.left + borderPadding.right; } width = PR_MAX(width, 0); @@ -4128,11 +4183,12 @@ nsTableFrame::CalcBorderBoxWidth(nsIPresContext* aPresContext, } nscoord -nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState) +nsTableFrame::CalcBorderBoxHeight(nsIPresContext* aPresContext, + const nsHTMLReflowState& aState) { nscoord height = aState.mComputedHeight; if (NS_AUTOHEIGHT != height) { - nsMargin borderPadding = aState.mComputedBorderPadding; + nsMargin borderPadding = GetContentAreaOffset(*aPresContext, &aState); height += borderPadding.top + borderPadding.bottom; } height = PR_MAX(0, height); @@ -4180,6 +4236,7 @@ nsTableFrame::CalcMinAndPreferredWidths(nsIPresContext* aPresContext, nscoord& aMinWidth, nscoord& aPrefWidth) { + if (!aPresContext) ABORT0(); aMinWidth = aPrefWidth = 0; nscoord spacingX = GetCellSpacingX(); @@ -4201,7 +4258,8 @@ nsTableFrame::CalcMinAndPreferredWidths(nsIPresContext* aPresContext, } // if it is not a degenerate table, add the last spacing on the right and the borderPadding if (numCols > 0) { - nscoord extra = spacingX + GetHorBorderPaddingWidth(aReflowState, this); + nsMargin childAreaOffset = GetChildAreaOffset(*aPresContext, &aReflowState); + nscoord extra = spacingX + childAreaOffset.left + childAreaOffset.right; aMinWidth += extra; aPrefWidth += extra; } @@ -4217,14 +4275,15 @@ nsTableFrame::CalcMinAndPreferredWidths(nsIPresContext* aPresContext, if (mTableLayoutStrategy && IsAutoLayout()) { float p2t; aPresContext->GetPixelsToTwips(&p2t); - aPrefWidth = mTableLayoutStrategy->CalcPctAdjTableWidth(aReflowState, availWidth, p2t); + aPrefWidth = mTableLayoutStrategy->CalcPctAdjTableWidth(*aPresContext, aReflowState, availWidth, p2t); } } } else { // a specified fix width becomes the min or preferred width nscoord compWidth = aReflowState.mComputedWidth; if ((NS_UNCONSTRAINEDSIZE != compWidth) && (0 != compWidth) && !isPctWidth) { - compWidth += GetHorBorderPaddingWidth(aReflowState, this); + nsMargin contentOffset = GetContentAreaOffset(*aPresContext, &aReflowState); + compWidth += contentOffset.left + contentOffset.right; aMinWidth = PR_MAX(aMinWidth, compWidth); aPrefWidth = PR_MAX(aMinWidth, compWidth); } @@ -4288,7 +4347,7 @@ nsTableFrame::DumpRowGroup(nsIPresContext* aPresContext, nsIFrame* aKidFrame) while (cellFrame) { nsIAtom* cellType; cellFrame->GetFrameType(&cellType); - if (nsLayoutAtoms::tableCellFrame == cellType) { + if (IS_TABLE_CELL(cellType)) { PRInt32 colIndex; ((nsTableCellFrame*)cellFrame)->GetColIndex(colIndex); printf("cell(%d)=%p ", colIndex, cellFrame); @@ -4557,6 +4616,2361 @@ PRInt32 nsTableFrame::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const return 0; } +/******************************************************************************** + * Collapsing Borders + * + * The CSS spec says to resolve border conflicts in this order: + * 1) any border with the style HIDDEN wins + * 2) the widest border with a style that is not NONE wins + * 3) the border styles are ranked in this order, highest to lowest precedence: + * double, solid, dashed, dotted, ridge, outset, groove, inset + * 4) borders that are of equal width and style (differ only in color) have this precedence: + * cell, row, rowgroup, col, colgroup, table + * 5) if all border styles are NONE, then that's the computed border style. + *******************************************************************************/ + +void +nsTableFrame::SetBCDamageArea(nsIPresContext& aPresContext, + const nsRect& aValue) +{ + nsRect newRect(aValue); + newRect.width = PR_MAX(1, newRect.width); + newRect.height = PR_MAX(1, newRect.height); + + if (!IsBorderCollapse()) { + NS_ASSERTION(PR_FALSE, "invalid call - not border collapse model"); + return; + } + SetNeedToCalcBCBorders(PR_TRUE); + // Get the property + BCPropertyData* value = (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, this, nsLayoutAtoms::tableBCProperty, PR_TRUE); + if (value) { + // for now just construct a union of the new and old damage areas + value->mDamageArea.UnionRect(value->mDamageArea, newRect); + } +} +/***************************************************************** + * BCMapCellIterator + ****************************************************************/ +struct BCMapCellInfo +{ + BCMapCellInfo(); + void Reset(); + + CellData* cellData; + nsCellMap* cellMap; + + nsTableRowGroupFrame* rg; + + nsTableRowFrame* topRow; + nsTableRowFrame* bottomRow; + + nsTableColGroupFrame* cg; + + nsTableColFrame* leftCol; + nsTableColFrame* rightCol; + + nsBCTableCellFrame* cell; + + PRInt32 rowIndex; + PRInt32 rowSpan; + PRInt32 colIndex; + PRInt32 colSpan; + + PRPackedBool rgTop; + PRPackedBool rgBottom; + PRPackedBool cgLeft; + PRPackedBool cgRight; +}; + +BCMapCellInfo::BCMapCellInfo() +{ + Reset(); +} + +void BCMapCellInfo::Reset() +{ + cellData = nsnull; + rg = nsnull; + topRow = nsnull; + bottomRow = nsnull; + cg = nsnull; + leftCol = nsnull; + rightCol = nsnull; + cell = nsnull; + rowIndex = rowSpan = colIndex = colSpan = 0; + rgTop = rgBottom = cgLeft = cgRight = PR_FALSE; +} + +class BCMapCellIterator +{ +public: + BCMapCellIterator(nsTableFrame& aTableFrame, + const nsRect& aDamageArea); + + void First(BCMapCellInfo& aMapCellInfo); + + void Next(BCMapCellInfo& aMapCellInfo); + + void PeekRight(BCMapCellInfo& aRefInfo, + PRUint32 aRowIndex, + BCMapCellInfo& aAjaInfo); + + void PeekBottom(BCMapCellInfo& aRefInfo, + PRUint32 aColIndex, + BCMapCellInfo& aAjaInfo); + + PRBool IsNewRow() { return mIsNewRow; } + + nsTableRowFrame* GetPrevRow() const { return mPrevRow; } + + PRInt32 mRowGroupStart; + PRInt32 mRowGroupEnd; + PRBool mAtEnd; + nsCellMap* mCellMap; + +private: + void SetInfo(nsTableRowFrame* aRow, + PRInt32 aColIndex, + CellData* aCellData, + BCMapCellInfo& aMapInfo, + nsCellMap* aCellMap = nsnull); + + PRBool SetNewRow(nsTableRowFrame* row = nsnull); + PRBool SetNewRowGroup(); + + nsTableFrame& mTableFrame; + nsTableCellMap* mTableCellMap; + nsVoidArray mRowGroups; + nsTableRowGroupFrame* mRowGroup; + PRInt32 mRowGroupIndex; + PRUint32 mNumRows; + nsTableRowFrame* mRow; + nsTableRowFrame* mPrevRow; + PRBool mIsNewRow; + PRInt32 mRowIndex; + PRUint32 mNumCols; + PRInt32 mColIndex; + nsPoint mAreaStart; + nsPoint mAreaEnd; +}; + +BCMapCellIterator::BCMapCellIterator(nsTableFrame& aTableFrame, + const nsRect& aDamageArea) +:mTableFrame(aTableFrame) +{ + mTableCellMap = aTableFrame.GetCellMap(); + + mAreaStart.x = aDamageArea.x; + mAreaStart.y = aDamageArea.y; + mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1; + mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1; + + mNumRows = mTableFrame.GetRowCount(); + mRow = nsnull; + mRowIndex = 0; + mNumCols = mTableFrame.GetColCount(); + mColIndex = 0; + mRowGroupIndex = -1; + + // Get the ordered row groups + PRUint32 numRowGroups; + aTableFrame.OrderRowGroups(mRowGroups, numRowGroups, nsnull); + + mAtEnd = PR_TRUE; // gets reset when First() is called +} + +void +BCMapCellIterator::SetInfo(nsTableRowFrame* aRow, + PRInt32 aColIndex, + CellData* aCellData, + BCMapCellInfo& aCellInfo, + nsCellMap* aCellMap) +{ + aCellInfo.cellData = aCellData; + aCellInfo.cellMap = (aCellMap) ? aCellMap : mCellMap; + aCellInfo.colIndex = aColIndex; + + // row frame info + aCellInfo.rowIndex = 0; + if (aRow) { + aCellInfo.topRow = aRow; + aCellInfo.rowIndex = aRow->GetRowIndex(); + } + + // cell frame info + aCellInfo.cell = nsnull; + aCellInfo.rowSpan = 1; + aCellInfo.colSpan = 1; + if (aCellData) { + aCellInfo.cell = (nsBCTableCellFrame*)aCellData->GetCellFrame(); + if (aCellInfo.cell) { + if (!aCellInfo.topRow) { + aCellInfo.cell->GetParent((nsIFrame**)&aCellInfo.topRow); if (!aCellInfo.topRow) ABORT0(); + aCellInfo.rowIndex = aCellInfo.topRow->GetRowIndex(); + } + PRBool ignore; + PRInt32 rgRowIndex = aCellInfo.rowIndex - mRowGroupStart; + aCellInfo.colSpan = mCellMap->GetEffectiveColSpan(*mTableCellMap, rgRowIndex, aColIndex, ignore); + aCellInfo.rowSpan = mCellMap->GetRowSpan(*mTableCellMap, rgRowIndex, aColIndex, PR_TRUE, ignore); + } + } + if (!aCellInfo.topRow) { + aCellInfo.topRow = mRow; + } + + if (1 == aCellInfo.rowSpan) { + aCellInfo.bottomRow = aCellInfo.topRow; + } + else { + aCellInfo.bottomRow = aCellInfo.topRow->GetNextRow(); + if (aCellInfo.bottomRow) { + for (PRInt32 spanX = 2; aCellInfo.bottomRow && (spanX < aCellInfo.rowSpan); spanX++) { + aCellInfo.bottomRow = aCellInfo.bottomRow->GetNextRow(); + } + NS_ASSERTION(aCellInfo.bottomRow, "program error"); + } + else { + NS_ASSERTION(PR_FALSE, "error in cell map"); + aCellInfo.rowSpan = 1; + aCellInfo.bottomRow = aCellInfo.topRow; + } + } + + // row group frame info + PRUint32 rgStart = mRowGroupStart; + PRUint32 rgEnd = mRowGroupEnd; + nsIFrame* parentFrame = nsnull; + aCellInfo.topRow->GetParent((nsIFrame**)&parentFrame); + aCellInfo.rg = mTableFrame.GetRowGroupFrame(parentFrame); + if (aCellInfo.rg != mRowGroup) { + rgStart = aCellInfo.rg->GetStartRowIndex(); + rgEnd = rgStart + aCellInfo.rg->GetRowCount() - 1; + } + PRUint32 rowIndex = aCellInfo.topRow->GetRowIndex(); + aCellInfo.rgTop = (rgStart == rowIndex); + aCellInfo.rgBottom = (rgEnd == rowIndex + aCellInfo.rowSpan - 1); + + // col frame info + aCellInfo.leftCol = mTableFrame.GetColFrame(aColIndex); if (!aCellInfo.leftCol) ABORT0(); + + aCellInfo.rightCol = aCellInfo.leftCol; + if (aCellInfo.colSpan > 1) { + for (PRInt32 spanX = 1; spanX < aCellInfo.colSpan; spanX++) { + nsTableColFrame* colFrame = aCellInfo.rightCol->GetNextCol(); + if (colFrame) { + aCellInfo.rightCol = colFrame; + } + else { + NS_ASSERTION(PR_FALSE, "program error"); + break; + } + } + } + + // col group frame info + aCellInfo.leftCol->GetParent((nsIFrame**)&aCellInfo.cg); + PRInt32 cgStart = aCellInfo.cg->GetStartColumnIndex(); + PRInt32 cgEnd = PR_MAX(0, cgStart + aCellInfo.cg->GetColCount() - 1); + aCellInfo.cgLeft = (cgStart == aColIndex); + aCellInfo.cgRight = (cgEnd == aColIndex + (PRInt32)aCellInfo.colSpan - 1); +} + +PRBool +BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow) +{ + mPrevRow = mRow; + if (aRow) { + mRow = aRow; + } + else if (mRow) { + mRow = mRow->GetNextRow(); + } + if (mRow) { + mRowIndex = mRow->GetRowIndex(); + // get to the first entry with an originating cell + PRInt32 rgRowIndex = mRowIndex - mRowGroupStart; + nsVoidArray* row = (nsVoidArray*)mCellMap->mRows.ElementAt(rgRowIndex); if (!row) ABORT1(PR_FALSE); + PRInt32 rowSize = row->Count(); + for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) { + CellData* cellData = (mColIndex < rowSize) ? (CellData*)row->ElementAt(mColIndex) : nsnull; + if (!cellData) { // add a dead cell data + nsRect damageArea; + cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT1(PR_FALSE); + } + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { + break; + } + } + mIsNewRow = PR_TRUE; + } + else { + NS_ASSERTION(PR_FALSE, "program error"); + mAtEnd = PR_TRUE; + } + return !mAtEnd; +} + +PRBool +BCMapCellIterator::SetNewRowGroup() +{ + mRowGroupIndex++; + PRInt32 numRowGroups = mRowGroups.Count(); + for (PRInt32 rgX = mRowGroupIndex; (rgX < numRowGroups) && !mAtEnd; rgX++) { + nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(mRowGroupIndex); if (!frame) ABORT1(PR_FALSE); + mRowGroup = mTableFrame.GetRowGroupFrame(frame); if (!mRowGroup) ABORT1(PR_FALSE); + mRowGroupStart = mRowGroup->GetStartRowIndex(); + mRowGroupEnd = mRowGroupStart + mRowGroup->GetRowCount() - 1; + if (mRowGroupEnd >= 0) { + mCellMap = mTableCellMap->GetMapFor(*mRowGroup); + if (SetNewRow(mRowGroup->GetFirstRow())) { + return PR_TRUE; + } + else mAtEnd = PR_TRUE; + } + } + mAtEnd = PR_TRUE; + return !mAtEnd; +} + +void +BCMapCellIterator::First(BCMapCellInfo& aMapInfo) +{ + mAtEnd = PR_FALSE; + aMapInfo.Reset(); + + SetNewRowGroup(); // could set mAtEnd + while (!mAtEnd) { + if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { + CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, mAreaStart.y - mRowGroupStart, mAreaStart.x, PR_FALSE); + if (cellData) { + if (!cellData->IsOrig()) { + // if the start data does not have an originating cell, adjust it to have one + if (cellData->IsRowSpan()) { + mAreaStart.y -= cellData->GetRowSpanOffset(); + NS_ASSERTION(mAreaStart.y >= 0, "program error"); + } + if (cellData->IsColSpan()) { + mAreaStart.x -= cellData->GetColSpanOffset(); + NS_ASSERTION(mAreaStart.x >= 0, "program error"); + } + cellData = mCellMap->GetDataAt(*mTableCellMap, mAreaStart.y - mRowGroupStart, mAreaStart.x, PR_FALSE); + } + if (cellData && cellData->IsOrig()) { + SetInfo(mRow, mAreaStart.x, cellData, aMapInfo); + break; + } + else mAtEnd = PR_TRUE; + } + else mAtEnd = PR_TRUE; + } + SetNewRowGroup(); // could set mAtEnd + } +} + +void +BCMapCellIterator::Next(BCMapCellInfo& aMapInfo) +{ + if (mAtEnd) ABORT0(); + aMapInfo.Reset(); + + mIsNewRow = PR_FALSE; + mColIndex++; + while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) { + for (; mColIndex <= mAreaEnd.x; mColIndex++) { + PRInt32 rgRowIndex = mRowIndex - mRowGroupStart; + CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, mColIndex, PR_FALSE); + if (!cellData) { // add a dead cell data + nsRect damageArea; + cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + if (cellData && (cellData->IsOrig() || cellData->IsDead())) { + SetInfo(mRow, mColIndex, cellData, aMapInfo); + return; + } + } + if (mRowIndex >= mRowGroupEnd) { + SetNewRowGroup(); // could set mAtEnd + } + else { + SetNewRow(); // could set mAtEnd + } + } + mAtEnd = PR_TRUE; +} + +void +BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo, + PRUint32 aRowIndex, + BCMapCellInfo& aAjaInfo) +{ + aAjaInfo.Reset(); + PRInt32 colIndex = aRefInfo.colIndex + aRefInfo.colSpan; + PRUint32 rgRowIndex = aRowIndex - mRowGroupStart; + + CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, colIndex, PR_FALSE); + if (!cellData) { // add a dead cell data + NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error"); + nsRect damageArea; + cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + nsTableRowFrame* row = nsnull; + if (cellData->IsRowSpan()) { + rgRowIndex -= cellData->GetRowSpanOffset(); + cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, colIndex, PR_FALSE); if (!cellData) ABORT0(); + } + else { + row = mRow; + } + SetInfo(row, colIndex, cellData, aAjaInfo); +} + +void +BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo, + PRUint32 aColIndex, + BCMapCellInfo& aAjaInfo) +{ + aAjaInfo.Reset(); + PRInt32 rowIndex = aRefInfo.rowIndex + aRefInfo.rowSpan; + PRInt32 rgRowIndex = rowIndex - mRowGroupStart; + nsTableRowGroupFrame* rg = mRowGroup; + nsCellMap* cellMap = mCellMap; + nsTableRowFrame* nextRow = nsnull; + if (rowIndex > mRowGroupEnd) { + nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(mRowGroupIndex + 1); if (!frame) ABORT0(); + rg = mTableFrame.GetRowGroupFrame(frame); + if (rg) { + cellMap = mTableCellMap->GetMapFor(*rg); + rgRowIndex = 0; + nextRow = rg->GetFirstRow(); + } + else return; + } + else { + // get the row within the same row group + nextRow = mRow; + for (PRInt32 i = 0; i < aRefInfo.rowSpan; i++) { + nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0(); + } + } + + CellData* cellData = cellMap->GetDataAt(*mTableCellMap, rgRowIndex, aColIndex, PR_FALSE); + if (!cellData) { // add a dead cell data + NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error"); + nsRect damageArea; + cellData = cellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0(); + } + if (cellData->IsColSpan()) { + aColIndex -= cellData->GetColSpanOffset(); + cellData = cellMap->GetDataAt(*mTableCellMap, rowIndex, aColIndex, PR_FALSE); + } + SetInfo(nextRow, aColIndex, cellData, aAjaInfo, cellMap); +} + +// Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID) +// will return the priority of NS_STYLE_BORDER_STYLE_SOLID. +static PRUint8 styleToPriority[13] = { 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 7, 1, 2 }; + +// Set aStyle and aWidth given aStyleBorder and aSide +static void +GetStyleInfo(const nsIFrame& aFrame, + PRUint8 aSide, + PRUint8& aStyle, + nscolor& aColor, + PRBool aIgnoreIfRules = PR_FALSE, + nscoord* aWidth = nsnull, + float* aTwipsToPixels = nsnull) +{ + const nsStyleBorder* styleData; + aFrame.GetStyleData(eStyleStruct_Border, (const nsStyleStruct *&)styleData); if (!styleData) ABORT0(); + + aStyle = styleData->GetBorderStyle(aSide); + + // if the rules mask is set, set the style either to none or remove the mask + if (NS_STYLE_BORDER_STYLE_RULES_MASK & aStyle) { + if (aIgnoreIfRules) { + aStyle = NS_STYLE_BORDER_STYLE_NONE; + aColor = 0; + if (aWidth) { + *aWidth = 0; + } + return; + } + else { + aStyle &= ~NS_STYLE_BORDER_STYLE_RULES_MASK; + } + } + + if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { + if (aWidth) { + *aWidth = 0; + aColor = 0; + } + return; + } + + if ((NS_STYLE_BORDER_STYLE_INSET == aStyle) || + (NS_STYLE_BORDER_STYLE_BG_INSET == aStyle)) { + aStyle = NS_STYLE_BORDER_STYLE_GROOVE; + } + else if ((NS_STYLE_BORDER_STYLE_OUTSET == aStyle) || + (NS_STYLE_BORDER_STYLE_BG_OUTSET == aStyle)) { + aStyle = NS_STYLE_BORDER_STYLE_RIDGE; + } + PRBool transparent, foreground; + styleData->GetBorderColor(aSide, aColor, transparent, foreground); + if (foreground) { + nsCOMPtr styleContext; + aFrame.GetStyleContext(getter_AddRefs(styleContext)); if(!styleContext) ABORT0(); + const nsStyleColor* colorStyle = (const nsStyleColor*)styleContext->GetStyleData(eStyleStruct_Color); + aColor = colorStyle->mColor; + } + if (aWidth && aTwipsToPixels) { + *aWidth = 0; + nscoord width; + styleData->CalcBorderFor(&aFrame, aSide, width); + *aWidth = NSToCoordRound(*aTwipsToPixels * (float)width); + } +} + +static PRBool +CalcDominateBorder(PRBool aIsCorner, + BCBorderOwner aOwner1, + PRUint8 aStyle1, + PRUint16 aWidth1, + nscolor aColor1, + BCBorderOwner aOwner2, + PRUint8 aStyle2, + PRUint16 aWidth2, + nscolor aColor2, + BCBorderOwner& aDomOwner, + PRUint8& aDomStyle, + PRUint16& aDomWidth, + nscolor& aDomColor, + PRBool aSecondIsHorizontal) +{ + PRBool firstDominates = PR_TRUE; + if (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle1) { + firstDominates = (aIsCorner) ? PR_FALSE : PR_TRUE; + } + else if (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle2) { + firstDominates = (aIsCorner) ? PR_TRUE : PR_FALSE; + } + else if (aWidth1 < aWidth2) { + firstDominates = PR_FALSE; + } + else if (aWidth1 == aWidth2) { + if (styleToPriority[aStyle1] < styleToPriority[aStyle2]) { + firstDominates = PR_FALSE; + } + else if (styleToPriority[aStyle1] == styleToPriority[aStyle2]) { + if (aOwner1 == aOwner2) { + firstDominates = !aSecondIsHorizontal; + } + else if (aOwner1 < aOwner2) { + firstDominates = PR_FALSE; + } + } + } + if (firstDominates) { + aDomOwner = aOwner1; + aDomStyle = aStyle1; + aDomWidth = aWidth1; + aDomColor = aColor1; + } + else { + aDomOwner = aOwner2; + aDomStyle = aStyle2; + aDomWidth = aWidth2; + aDomColor = aColor2; + } + return firstDominates; +} + +// calc the dominate border by considering the table, row/col group, row/col, cell, +static void +CalcDominateBorder(const nsIFrame* aTableFrame, + const nsIFrame* aColGroupFrame, + const nsIFrame* aColFrame, + const nsIFrame* aRowGroupFrame, + const nsIFrame* aRowFrame, + const nsIFrame* aCellFrame, + PRBool aIgnoreIfRules, + PRUint8 aSide, + PRBool aAja, + float aTwipsToPixels, + BCBorderOwner& aDomElem, + PRUint8& aDomStyle, + PRUint16& aDomWidth, + nscolor& aDomColor) +{ + PRUint8 style; + nscolor color; + aDomStyle = NS_STYLE_BORDER_STYLE_NONE; + nscoord width; + aDomWidth = 0; + PRBool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide); + + // start with the table as dominate if present + if (aTableFrame) { + GetStyleInfo(*aTableFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = eTableOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + // see if the col row group is dominate + if (aColGroupFrame) { + GetStyleInfo(*aColGroupFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the col is dominate + if (aColFrame) { + GetStyleInfo(*aColFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && !horizontal) ? eAjaColOwner : eColOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the row row group is dominate + if (aRowGroupFrame) { + GetStyleInfo(*aRowGroupFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the row is dominate + if (aRowFrame) { + GetStyleInfo(*aRowFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja && horizontal) ? eAjaRowOwner : eRowOwner; + if (NS_STYLE_BORDER_STYLE_HIDDEN == style) { + return; + } + } + } + // see if the cell is dominate + if (aCellFrame) { + GetStyleInfo(*aCellFrame, aSide, style, color, aIgnoreIfRules, &width, &aTwipsToPixels); + if ((NS_STYLE_BORDER_STYLE_HIDDEN == style) || + (width > aDomWidth) || + ((width == aDomWidth) && + (styleToPriority[style] >= styleToPriority[aDomStyle]))) { + aDomStyle = style; + aDomWidth = width; + aDomColor = color; + aDomElem = (aAja) ? eAjaCellOwner : eCellOwner; + } + } +} + +static PRBool +Perpendicular(PRUint8 aSide1, + PRUint8 aSide2) +{ + switch (aSide1) { + case NS_SIDE_TOP: + return (NS_SIDE_BOTTOM != aSide2); + case NS_SIDE_RIGHT: + return (NS_SIDE_LEFT != aSide2); + case NS_SIDE_BOTTOM: + return (NS_SIDE_TOP != aSide2); + default: // NS_SIDE_LEFT + return (NS_SIDE_RIGHT != aSide2); + } +} + +// XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1 +struct BCCornerInfo +{ + BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerSide = ownerElem = subSide = + subElem = hasDashDot = numSegs = bevel = 0; + ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; } + void Set(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor); + + void Update(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor); + + nscolor ownerColor; // color of borderOwner + PRUint16 ownerWidth; // pixel width of borderOwner + PRUint16 subWidth; // pixel width of the largest border intersecting the border perpendicular + // to ownerSide + PRUint32 ownerSide:2; // side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border owning + // the corner relative to the corner + PRUint32 ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner + PRUint32 ownerStyle:8; // border style of ownerElem + PRUint32 subSide:2; // side of border with subWidth relative to the corner + PRUint32 subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner + PRUint32 subStyle:8; // border style of subElem + PRUint32 hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled + PRUint32 numSegs:3; // number of segments entering corner + PRUint32 bevel:1; // is the corner beveled (uses the above two fields together with subWidth) + PRUint32 unused:1; +}; + +void +BCCornerInfo::Set(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor) +{ + ownerElem = aBorderOwner; + ownerStyle = aOwnerBStyle; + ownerWidth = aOwnerWidth; + ownerColor = aOwnerColor; + ownerSide = aSide; + hasDashDot = 0; + numSegs = 0; + if (aOwnerWidth > 0) { + numSegs++; + hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aOwnerBStyle) || + (NS_STYLE_BORDER_STYLE_DOTTED == aOwnerBStyle); + } + bevel = 0; + subWidth = 0; + // the following will get set later + subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT; + subElem = eTableOwner; + subStyle = NS_STYLE_BORDER_STYLE_SOLID; +} + +void +BCCornerInfo::Update(PRUint8 aSide, + BCBorderOwner aBorderOwner, + PRUint8 aOwnerBStyle, + nscoord aOwnerWidth, + nscolor aOwnerColor) +{ + PRBool existingWins = PR_FALSE; + if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet + Set(aSide, aBorderOwner, aOwnerBStyle, aOwnerWidth, aOwnerColor); + } + else { + PRBool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner + PRUint8 oldElem = ownerElem; + PRUint8 oldSide = ownerSide; + PRUint8 oldStyle = ownerStyle; + nscoord oldWidth = ownerWidth; + BCBorderOwner tempBorderOwner = (BCBorderOwner)ownerElem; + PRUint8 tempStyle = ownerStyle; + existingWins = + CalcDominateBorder(PR_TRUE, (BCBorderOwner)ownerElem, ownerStyle, ownerWidth, ownerColor, + (BCBorderOwner)aBorderOwner, aOwnerBStyle, aOwnerWidth, aOwnerColor, + tempBorderOwner, tempStyle, ownerWidth, ownerColor, horizontal); + ownerElem = tempBorderOwner; + ownerStyle = tempStyle; + if (existingWins) { // existing corner is dominate + if (::Perpendicular(ownerSide, aSide)) { + // see if the new sub info replaces the old + nscolor color; + tempBorderOwner = (BCBorderOwner)ownerElem; + PRUint8 tempStyle = subStyle; + PRBool firstWins = + CalcDominateBorder(PR_TRUE, (BCBorderOwner)subElem, subStyle, subWidth, color, + (BCBorderOwner)aBorderOwner, aOwnerBStyle, aOwnerWidth, aOwnerColor, + tempBorderOwner, tempStyle, subWidth, color, horizontal); + subElem = tempBorderOwner; + subStyle = tempStyle; + if (firstWins) { + subSide = aSide; + } + } + } + else { // input args are dominate + ownerSide = aSide; + if (::Perpendicular(oldSide, ownerSide)) { + subElem = oldElem; + subSide = oldSide; + subStyle = oldStyle; + subWidth = oldWidth; + } + } + if (aOwnerWidth > 0) { + numSegs++; + if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aOwnerBStyle) || + (NS_STYLE_BORDER_STYLE_DOTTED == aOwnerBStyle))) { + hasDashDot = 1; + } + } + + // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner + bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot); + } +} + +struct BCCorners +{ + BCCorners(PRInt32 aNumCorners, + PRInt32 aStartIndex); + + ~BCCorners() { delete [] corners; } + + BCCornerInfo& operator [](PRInt32 i) const + { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); + return corners[i - startIndex]; } + + PRInt32 startIndex; + PRInt32 endIndex; + BCCornerInfo* corners; +}; + +BCCorners::BCCorners(PRInt32 aNumCorners, + PRInt32 aStartIndex) +{ + NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error"); + startIndex = aStartIndex; + endIndex = aStartIndex + aNumCorners - 1; + corners = new BCCornerInfo[aNumCorners]; +} + +struct BCCellBorder +{ + BCCellBorder() { Reset(); } + void Reset(PRUint32 aIndex = 0, PRUint32 aSpan = 1); + nscolor color; + PRUint16 index; // y index, not used for vertical borders + PRUint16 span; // row span + PRUint16 width; + PRUint8 style; +}; + +void +BCCellBorder::Reset(PRUint32 aIndex, + PRUint32 aSpan) +{ + style = color = width = -1; + index = (PRUint16)aIndex; + span = (PRUint16)aSpan; +} + +struct BCCellBorders +{ + BCCellBorders(PRInt32 aNumBorders, + PRInt32 aStartIndex); + + ~BCCellBorders() { delete [] borders; } + + BCCellBorder& operator [](PRInt32 i) const + { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); + return borders[i - startIndex]; } + + PRInt32 startIndex; + PRInt32 endIndex; + BCCellBorder* borders; +}; + +BCCellBorders::BCCellBorders(PRInt32 aNumBorders, + PRInt32 aStartIndex) +{ + NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error"); + startIndex = aStartIndex; + endIndex = aStartIndex + aNumBorders - 1; + borders = new BCCellBorder[aNumBorders]; +} + +static PRBool +SetBorder(PRUint8 aOwnerBStyle, + PRUint16 aOwnerWidth, + nscolor aOwnerColor, + BCCellBorder& aBorder) +{ + PRBool changed = (aOwnerBStyle != aBorder.style) || (aOwnerWidth != aBorder.width) || + (aOwnerColor != aBorder.color); + aBorder.color = aOwnerColor; + aBorder.width = aOwnerWidth; + aBorder.style = aOwnerBStyle; + + return changed; +} + +static PRBool +SetHorBorder(PRUint8 aOwnerBStyle, + PRUint16 aOwnerWidth, + PRUint32 aOwnerColor, + const BCCornerInfo& aCorner, + BCCellBorder& aBorder) +{ + PRBool startSeg = ::SetBorder(aOwnerBStyle, aOwnerWidth, aOwnerColor, aBorder); + if (!startSeg) { + startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide)); + } + return startSeg; +} + +// Make the damage area larger on the top and bottom by at least one row and on the left and right +// at least one column. This is done so that adjacent elements are part of the border calculations. +// The extra segments and borders outside the actual damage area will not be updated in the cell map, +// because they in turn would need info from adjacent segments outside the damage area to be accurate. +void +nsTableFrame::ExpandBCDamageArea(nsRect& aRect) const +{ + PRInt32 numRows = GetRowCount(); + PRInt32 numCols = GetColCount(); + + PRInt32 dStartX = aRect.x; + PRInt32 dEndX = aRect.XMost() - 1; + PRInt32 dStartY = aRect.y; + PRInt32 dEndY = aRect.YMost() - 1; + + // expand the damage area in each direction + if (dStartX > 0) { + dStartX--; + } + if (dEndX < (numCols - 1)) { + dEndX++; + } + if (dStartY > 0) { + dStartY--; + } + if (dEndY < (numRows - 1)) { + dEndY++; + } + // Check the damage area so that there are no cells spanning in or out. If there are any then + // make the damage area as big as the table, similarly to the way the cell map decides whether + // to rebuild versus expand. This could be optimized to expand to the smallest area that contains + // no spanners, but it may not be worth the effort in general, and it would need to be done in the + // cell map as well. + if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) { + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); + PRBool haveSpanner = PR_FALSE; + // Get the ordered row groups + PRUint32 numRowGroups; + nsVoidArray rowGroups; + OrderRowGroups(rowGroups, numRowGroups, nsnull); + for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { + nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX); + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0(); + PRInt32 rgStartY = rgFrame->GetStartRowIndex(); + PRInt32 rgEndY = rgStartY + rgFrame->GetRowCount() - 1; + if (dEndY < rgStartY) + break; + nsCellMap* cellMap = tableCellMap->GetMapFor(*rgFrame); if (!cellMap) ABORT0(); + // check for spanners from above and below + if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) { + nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(dStartY - rgStartY); if (!row) ABORT0(); + for (PRInt32 x = dStartX; x <= dEndX; x++) { + CellData* cellData = (row->Count() > x) ? (CellData*)row->ElementAt(x) : nsnull; + if (cellData && (cellData->IsRowSpan())) { + haveSpanner = PR_TRUE; + break; + } + } + if (dEndY < rgEndY) { + row = (nsVoidArray*)cellMap->mRows.ElementAt(dEndY + 1 - rgStartY); if (!row) ABORT0(); + for (PRInt32 x = dStartX; x <= dEndX; x++) { + CellData* cellData = (row->Count() > x) ? (CellData*)row->ElementAt(x) : nsnull; + if (cellData && (cellData->IsRowSpan())) { + haveSpanner = PR_TRUE; + break; + } + } + } + } + // check for spanners on the left and right + PRInt32 iterStartY = -1; + PRInt32 iterEndY = -1; + if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) { + // the damage area starts in the row group + iterStartY = dStartY; + iterEndY = PR_MIN(dEndY, rgEndY); + } + else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) { + // the damage area ends in the row group + iterStartY = rgStartY; + iterEndY = PR_MIN(dEndY, rgStartY); + } + else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) { + // the damage area contains the row group + iterStartY = rgStartY; + iterEndY = rgEndY; + } + if ((iterStartY >= 0) && (iterEndY >= 0)) { + for (PRInt32 y = iterStartY; y <= iterEndY; y++) { + nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(y - rgStartY); if (!row) ABORT0(); + CellData* cellData = (CellData*)row->ElementAt(dStartX); + if (cellData && (cellData->IsColSpan())) { + haveSpanner = PR_TRUE; + break; + } + if (dEndX < (numCols - 1)) { + cellData = (row->Count() > dEndX) ? (CellData*)row->ElementAt(dEndX + 1) : nsnull; + if (cellData && (cellData->IsColSpan())) { + haveSpanner = PR_TRUE; + break; + } + } + } + } + } + } + aRect.x = dStartX; + aRect.y = dStartY; + aRect.width = 1 + dEndX - dStartX; + aRect.height = 1 + dEndY - dStartY; +} + +#define MAX_TABLE_BORDER_WIDTH 256 +static PRUint8 +LimitBorderWidth(PRUint16 aWidth) +{ + return PR_MIN(MAX_TABLE_BORDER_WIDTH, aWidth); +} + +/* Here is the order for storing border edges in the cell map as a cell is processed. There are + n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell. + + 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since + a bottom edge from a cell above will take care of it. + 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since + a right edge from a cell to the left will take care of it. + 3) Store the right edge (or edges if a row span) + 4) Store the bottom edge (or edges if a col span) + + Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner + calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries + would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing + borders already have about an extra 8 byte per cell map entry overhead (this could be + reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the + rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the + left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border + edges per cell and n=rowspan left and right border edges per cell. + + 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table. + Never store the top-right corner, since it will get stored as a right-top corner. + 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner, + since it will get stored as a bottom-left corner. + 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on + the top edge of the table. Never store the right-bottom corner since it will get stored as a + bottom-right corner. + 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it + otherwise, since it will get stored as either a right-top corner by a cell below or + a bottom-left corner from a cell to the right. + 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits + the top side of a colspan in its interior. Never store the corner otherwise, since it will + get stored as a right-top corner by a cell from below. + */ + +#define TOP_DAMAGED(aRowIndex) ((aRowIndex) >= propData->mDamageArea.y) +#define RIGHT_DAMAGED(aColIndex) ((aColIndex) <= propData->mDamageArea.XMost()) +#define BOTTOM_DAMAGED(aRowIndex) ((aRowIndex) <= propData->mDamageArea.YMost()) +#define LEFT_DAMAGED(aColIndex) ((aColIndex) >= propData->mDamageArea.x) + +// Calc the dominate border at every cell edge and corner within the current damage area +void +nsTableFrame::CalcBCBorders(nsIPresContext& aPresContext) +{ + nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); + PRInt32 numRows = GetRowCount(); + PRInt32 numCols = GetColCount(); + + // Get the property holding the table damage area and border widths + BCPropertyData* propData = + (BCPropertyData*)nsTableFrame::GetProperty(&aPresContext, this, nsLayoutAtoms::tableBCProperty, PR_FALSE); + if (!propData) ABORT0(); + // calculate an expanded damage area + nsRect damageArea(propData->mDamageArea); + ExpandBCDamageArea(damageArea); + + // segments that are on the table border edges need to be initialized only once + PRBool tableBorderReset[4]; + for (PRUint32 sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) { + tableBorderReset[sideX] = PR_FALSE; + } + GET_TWIPS_TO_PIXELS(&aPresContext, t2p); + + const nsStyleBorder* tableStyle = + (const nsStyleBorder*)mStyleContext->GetStyleData(eStyleStruct_Border); + + // vertical borders indexed in x-direction (cols) + BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); if (!lastVerBorders.borders) ABORT0(); + BCCellBorder lastTopBorder, lastBottomBorder; + // horizontal borders indexed in x-direction (cols) + BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); if (!lastBottomBorders.borders) ABORT0(); + PRBool startSeg; + + BCMapCellInfo info, ajaInfo; + BCBorderOwner owner, ajaOwner; + nscolor ownerColor, ajaColor; + PRUint8 ownerBStyle, ajaBStyle; + PRUint16 ownerWidth, ajaWidth; + PRInt32 cellEndRowIndex = -1; + PRInt32 cellEndColIndex = -1; + nscoord smallHalf, largeHalf; + BCCorners topCorners(damageArea.width + 1, damageArea.x); if (!topCorners.corners) ABORT0(); + BCCorners bottomCorners(damageArea.width + 1, damageArea.x); if (!bottomCorners.corners) ABORT0(); + nsTableRowFrame* prevRow = nsnull; + + BCMapCellIterator iter(*this, damageArea); + for (iter.First(info); !iter.mAtEnd; iter.Next(info)) { + + cellEndRowIndex = info.rowIndex + info.rowSpan - 1; + cellEndColIndex = info.colIndex + info.colSpan - 1; + PRBool isBottomRight = (info.rowIndex == (numRows - 1)) && (info.colIndex == (numCols - 1)); + + PRBool bottomRowSpan = PR_FALSE; + // see if lastTopBorder, lastBottomBorder need to be reset + if (iter.IsNewRow()) { + lastTopBorder.Reset(info.rowIndex, info.rowSpan); + lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan); + } + else if (info.colIndex > damageArea.x) { + BCCellBorder& prevBorder = lastBottomBorders[info.colIndex - 1]; + if (info.rowIndex > prevBorder.index - prevBorder.span) { + // the top border's left edge butts against the middle of a rowspan + lastTopBorder.Reset(info.rowIndex, info.rowSpan); + } + if (prevBorder.index > (cellEndRowIndex + 1)) { + // the bottom border's left edge butts against the middle of a rowspan + lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan); + bottomRowSpan = PR_TRUE; + } + } + + // find the dominate border considernig the cell's top border and the table, row group, row + // if the border is at the top of the table, otherwise it was processed in a previous row + if (0 == info.rowIndex) { + if (!tableBorderReset[NS_SIDE_TOP]) { + propData->mTopBorderWidth = 0; + tableBorderReset[NS_SIDE_TOP] = PR_TRUE; + } + for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) { + nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0(); + nsIFrame* cgFrame; + colFrame->GetParent(&cgFrame); if (!cgFrame) ABORT0(); + CalcDominateBorder(this, cgFrame, colFrame, info.rg, info.topRow, info.cell, PR_TRUE, NS_SIDE_TOP, + PR_FALSE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + // update/store the top left & top right corners of the seg + BCCornerInfo& tlCorner = topCorners[colX]; // top left + if (0 == colX) { + tlCorner.Set(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + } + else { + tlCorner.Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, 0, 0, colX, + tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel); + } + topCorners[colX + 1].Set(NS_SIDE_LEFT, owner, ownerBStyle, ownerWidth, ownerColor); // top right + // update lastTopBorder and see if a new segment starts + startSeg = SetHorBorder(ownerBStyle, ownerWidth, ownerColor, tlCorner, lastTopBorder); + // store the border segment in the cell map + tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *info.cellMap, 0, 0, colX, + 1, owner, ownerWidth, startSeg); + // update the affected borders of the cell, row, and table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(smallHalf, info.cell->GetBorderWidth(NS_SIDE_TOP))); + } + if (info.topRow) { + info.topRow->SetTopBCBorderWidth(PR_MAX(smallHalf, info.topRow->GetTopBCBorderWidth())); + } + propData->mTopBorderWidth = LimitBorderWidth(PR_MAX(propData->mTopBorderWidth, (PRUint8)ownerWidth)); + } + } + else { + // see if the top border needs to be the start of a segment due to a vertical border owning the corner + if (info.colIndex > 0) { + BCData& data = ((BCCellData*)info.cellData)->mData; + if (!data.IsTopStart()) { + PRUint8 cornerSide; + PRPackedBool bevel; + data.GetCorner(cornerSide, bevel); + if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) { + data.SetTopStart(PR_TRUE); + } + } + } + } + + // find the dominate border considernig the cell's left border and the table, col group, col + // if the border is at the left of the table, otherwise it was processed in a previous col + if (0 == info.colIndex) { + if (!tableBorderReset[NS_SIDE_LEFT]) { + propData->mLeftBorderWidth = 0; + tableBorderReset[NS_SIDE_LEFT] = PR_TRUE; + } + for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) { + nsTableRowFrame* rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow(); + CalcDominateBorder(this, info.cg, info.leftCol, info.rg, rowFrame, info.cell, PR_TRUE, NS_SIDE_LEFT, + PR_FALSE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + BCCornerInfo& tlCorner = (0 == rowX) ? topCorners[0] : bottomCorners[0]; // top left + tlCorner.Update(NS_SIDE_BOTTOM, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, iter.mRowGroupStart, rowX, + 0, tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel); + bottomCorners[0].Set(NS_SIDE_TOP, owner, ownerBStyle, ownerWidth, ownerColor); // bottom left + // update lastVerBordersBorder and see if a new segment starts + startSeg = SetBorder(ownerBStyle, ownerWidth, ownerColor, lastVerBorders[0]); + // store the border segment in the cell map + tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *info.cellMap, iter.mRowGroupStart, rowX, + info.colIndex, 1, owner, ownerWidth, startSeg); + // update the left border of the cell, col and table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_LEFT, PR_MAX(smallHalf, info.cell->GetBorderWidth(NS_SIDE_LEFT))); + } + if (info.leftCol) { + info.leftCol->SetLeftBorderWidth(PR_MAX(smallHalf, info.leftCol->GetLeftBorderWidth())); + } + propData->mLeftBorderWidth = LimitBorderWidth(PR_MAX(propData->mLeftBorderWidth, ownerWidth)); + } + } + + // find the dominate border considernig the cell's right border, adjacent cells and the table, row group, row + if (numCols == cellEndColIndex + 1) { // touches right edge of table + if (!tableBorderReset[NS_SIDE_RIGHT]) { + propData->mRightBorderWidth = 0; + tableBorderReset[NS_SIDE_RIGHT] = PR_TRUE; + } + for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) { + nsTableRowFrame* rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow(); + CalcDominateBorder(this, info.cg, info.rightCol, info.rg, rowFrame, info.cell, PR_TRUE, NS_SIDE_RIGHT, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + // update/store the top right & bottom right corners + BCCornerInfo& trCorner = (0 == rowX) ? topCorners[cellEndColIndex + 1] : bottomCorners[cellEndColIndex + 1]; + trCorner.Update(NS_SIDE_BOTTOM, owner, ownerBStyle, ownerWidth, ownerColor); // top right + tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, trCorner.ownerSide, trCorner.subWidth, trCorner.bevel); + BCCornerInfo& brCorner = bottomCorners[cellEndColIndex + 1]; + brCorner.Set(NS_SIDE_TOP, owner, ownerBStyle, ownerWidth, ownerColor); // bottom right + tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel); + // update lastVerBorders and see if a new segment starts + startSeg = SetBorder(ownerBStyle, ownerWidth, ownerColor, lastVerBorders[cellEndColIndex + 1]); + // store the border segment in the cell map and update cellBorders + tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, 1, owner, ownerWidth, startSeg); + // update the affected borders of the cell, col, and table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_RIGHT, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_RIGHT))); + } + if (info.rightCol) { + info.rightCol->SetRightBorderWidth(PR_MAX(largeHalf, info.rightCol->GetRightBorderWidth())); + } + propData->mRightBorderWidth = LimitBorderWidth(PR_MAX(propData->mRightBorderWidth, ownerWidth)); + } + } + else { + PRInt32 segLength = 0; + BCMapCellInfo priorAjaInfo; + for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX += segLength) { + iter.PeekRight(info, rowX, ajaInfo); + const nsIFrame* cg = (info.cgRight) ? info.cg : nsnull; + CalcDominateBorder(nsnull, cg, info.rightCol, nsnull, nsnull, info.cell, PR_FALSE, NS_SIDE_RIGHT, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + cg = (ajaInfo.cgLeft) ? ajaInfo.cg : nsnull; + CalcDominateBorder(nsnull, cg, ajaInfo.leftCol, nsnull, nsnull, ajaInfo.cell, PR_FALSE, NS_SIDE_LEFT, + PR_FALSE, t2p, ajaOwner, ajaBStyle, ajaWidth, ajaColor); + CalcDominateBorder(PR_FALSE, owner, ownerBStyle, ownerWidth, ownerColor, ajaOwner, ajaBStyle, + ajaWidth, ajaColor, owner, ownerBStyle, ownerWidth, ownerColor, PR_FALSE); + segLength = PR_MAX(1, ajaInfo.rowIndex + ajaInfo.rowSpan - rowX); + segLength = PR_MIN(segLength, info.rowIndex + info.rowSpan - rowX); + + // update lastVerBorders and see if a new segment starts + startSeg = SetBorder(ownerBStyle, ownerWidth, ownerColor, lastVerBorders[cellEndColIndex + 1]); + // store the border segment in the cell map and update cellBorders + if (RIGHT_DAMAGED(cellEndColIndex) && TOP_DAMAGED(rowX) && BOTTOM_DAMAGED(rowX)) { + tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX, + cellEndColIndex, segLength, owner, ownerWidth, startSeg); + // update the borders of the cells and cols affected + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_RIGHT, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_RIGHT))); + } + if (info.rightCol) { + info.rightCol->SetRightBorderWidth(PR_MAX(largeHalf, info.rightCol->GetRightBorderWidth())); + } + if (ajaInfo.cell) { + ajaInfo.cell->SetBorderWidth(NS_SIDE_LEFT, PR_MAX(smallHalf, ajaInfo.cell->GetBorderWidth(NS_SIDE_LEFT))); + } + if (ajaInfo.leftCol) { + ajaInfo.leftCol->SetLeftBorderWidth(PR_MAX(smallHalf, ajaInfo.leftCol->GetLeftBorderWidth())); + } + } + // update the top right corner + PRBool hitsSpanOnRight = (rowX > ajaInfo.rowIndex) && (rowX < ajaInfo.rowIndex + ajaInfo.rowSpan); + BCCornerInfo* trCorner = ((0 == rowX) || hitsSpanOnRight) + ? &topCorners[cellEndColIndex + 1] : &bottomCorners[cellEndColIndex + 1]; + trCorner->Update(NS_SIDE_BOTTOM, owner, ownerBStyle, ownerWidth, ownerColor); + // if this is not the first time through, consider the segment to the right + if (rowX != info.rowIndex) { + const nsIFrame* rg = (priorAjaInfo.rgBottom) ? priorAjaInfo.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, priorAjaInfo.bottomRow, priorAjaInfo.cell, PR_FALSE, + NS_SIDE_BOTTOM, PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell, PR_FALSE, NS_SIDE_TOP, + PR_FALSE, t2p, ajaOwner, ajaBStyle, ajaWidth, ajaColor); + CalcDominateBorder(PR_FALSE, owner, ownerBStyle, ownerWidth, ownerColor, ajaOwner, ajaBStyle, + ajaWidth, ajaColor, owner, ownerBStyle, ownerWidth, ownerColor, PR_TRUE); + trCorner->Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + } + // store the top right corner in the cell map + if (RIGHT_DAMAGED(cellEndColIndex) && TOP_DAMAGED(rowX)) { + if (0 != rowX) { + tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, cellEndColIndex, + trCorner->ownerSide, trCorner->subWidth, trCorner->bevel); + } + // store any corners this cell spans together with the aja cell + for (PRInt32 rX = rowX + 1; rX < rowX + segLength; rX++) { + tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rX, + cellEndColIndex, trCorner->ownerSide, trCorner->subWidth, PR_FALSE); + } + } + // update bottom right corner, topCorners, bottomCorners + hitsSpanOnRight = (rowX + segLength < ajaInfo.rowIndex + ajaInfo.rowSpan); + BCCornerInfo& brCorner = (hitsSpanOnRight) ? topCorners[cellEndColIndex + 1] + : bottomCorners[cellEndColIndex + 1]; + brCorner.Set(NS_SIDE_TOP, owner, ownerBStyle, ownerWidth, ownerColor); + priorAjaInfo = ajaInfo; + } + } + for (PRInt32 colX = info.colIndex + 1; colX <= cellEndColIndex; colX++) { + lastVerBorders[colX].Reset(); + } + + // find the dominate border considernig the cell's bottom border, adjacent cells and the table, row group, row + if (numRows == cellEndRowIndex + 1) { // touches botom edge of table + if (!tableBorderReset[NS_SIDE_BOTTOM]) { + propData->mBottomBorderWidth = 0; + tableBorderReset[NS_SIDE_BOTTOM] = PR_TRUE; + } + for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) { + nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0(); + nsIFrame* cgFrame; + colFrame->GetParent(&cgFrame); if (!cgFrame) ABORT0(); + CalcDominateBorder(this, cgFrame, colFrame, info.rg, info.bottomRow, info.cell, PR_TRUE, NS_SIDE_BOTTOM, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + // update/store the bottom left & bottom right corners + BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left + blCorner.Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, blCorner.ownerSide, blCorner.subWidth, blCorner.bevel); + BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right + brCorner.Update(NS_SIDE_LEFT, owner, ownerBStyle, ownerWidth, ownerColor); + if (numCols == colX + 1) { // lower right corner of the table + tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel, isBottomRight); + } + // update lastBottomBorder and see if a new segment starts + startSeg = SetHorBorder(ownerBStyle, ownerWidth, ownerColor, blCorner, lastBottomBorder); + // store the border segment in the cell map and update cellBorders + tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, 1, owner, ownerWidth, startSeg); + // update the bottom borders of the cell, the bottom row, and the table + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_BOTTOM))); + } + if (info.bottomRow) { + info.bottomRow->SetBottomBCBorderWidth(PR_MAX(largeHalf, info.bottomRow->GetBottomBCBorderWidth())); + } + propData->mBottomBorderWidth = LimitBorderWidth(PR_MAX(propData->mBottomBorderWidth, ownerWidth)); + // update lastBottomBorders + lastBottomBorder.index = cellEndRowIndex + 1; + lastBottomBorder.span = info.rowSpan; + lastBottomBorders[colX] = lastBottomBorder; + } + } + else { + PRInt32 segLength = 0; + for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX += segLength) { + iter.PeekBottom(info, colX, ajaInfo); + const nsIFrame* rg = (info.rgBottom) ? info.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, info.bottomRow, info.cell, PR_FALSE, NS_SIDE_BOTTOM, + PR_TRUE, t2p, owner, ownerBStyle, ownerWidth, ownerColor); + rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull; + CalcDominateBorder(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell, PR_FALSE, NS_SIDE_TOP, + PR_FALSE, t2p, ajaOwner, ajaBStyle, ajaWidth, ajaColor); + CalcDominateBorder(PR_FALSE, owner, ownerBStyle, ownerWidth, ownerColor, ajaOwner, ajaBStyle, ajaWidth, + ajaColor, owner, ownerBStyle, ownerWidth, ownerColor, PR_TRUE); + segLength = PR_MAX(1, ajaInfo.colIndex + ajaInfo.colSpan - colX); + segLength = PR_MIN(segLength, info.colIndex + info.colSpan - colX); + + // update, store the bottom left corner + BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left + PRBool hitsSpanBelow = (colX > ajaInfo.colIndex) && (colX < ajaInfo.colIndex + ajaInfo.colSpan); + PRBool update = PR_TRUE; + if ((colX == info.colIndex) && (colX > damageArea.x)) { + PRInt32 prevRowIndex = lastBottomBorders[colX - 1].index; + if (prevRowIndex > cellEndRowIndex + 1) { // hits a rowspan on the right + update = PR_FALSE; // the corner was taken care of during the cell on the left + } + else if (prevRowIndex < cellEndRowIndex) { // spans below the cell to the left + topCorners[colX] = blCorner; + blCorner.Set(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + update = PR_FALSE; + } + } + if (update) { + blCorner.Update(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + } + if (BOTTOM_DAMAGED(cellEndRowIndex) && LEFT_DAMAGED(colX)) { + if (hitsSpanBelow) { + tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, colX, + blCorner.ownerSide, blCorner.subWidth, blCorner.bevel); + } + // store any corners this cell spans together with the aja cell + for (PRInt32 cX = colX + 1; cX < colX + segLength; cX++) { + BCCornerInfo& corner = bottomCorners[cX]; + corner.Set(NS_SIDE_RIGHT, owner, ownerBStyle, ownerWidth, ownerColor); + tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + cX, corner.ownerSide, corner.subWidth, PR_FALSE); + } + } + // update lastBottomBorders and see if a new segment starts + startSeg = SetHorBorder(ownerBStyle, ownerWidth, ownerColor, blCorner, lastBottomBorder); + lastBottomBorder.index = cellEndRowIndex + 1; + lastBottomBorder.span = info.rowSpan; + for (PRInt32 cX = colX; cX < colX + segLength; cX++) { + lastBottomBorders[cX] = lastBottomBorder; + } + + // store the border segment the cell map and update cellBorders + if (BOTTOM_DAMAGED(cellEndRowIndex) && LEFT_DAMAGED(colX) && RIGHT_DAMAGED(colX)) { + tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, + colX, segLength, owner, ownerWidth, startSeg); + // update the borders of the affected cells and rows + DivideBCBorderSize(ownerWidth, smallHalf, largeHalf); + if (info.cell) { + info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(largeHalf, info.cell->GetBorderWidth(NS_SIDE_BOTTOM))); + } + if (info.bottomRow) { + info.bottomRow->SetBottomBCBorderWidth(PR_MAX(largeHalf, info.bottomRow->GetBottomBCBorderWidth())); + } + if (ajaInfo.cell) { + ajaInfo.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(smallHalf, ajaInfo.cell->GetBorderWidth(NS_SIDE_TOP))); + } + if (ajaInfo.topRow) { + ajaInfo.topRow->SetTopBCBorderWidth(PR_MAX(smallHalf, ajaInfo.topRow->GetTopBCBorderWidth())); + } + } + // update bottom right corner + BCCornerInfo& brCorner = bottomCorners[colX + segLength]; + brCorner.Update(NS_SIDE_LEFT, owner, ownerBStyle, ownerWidth, ownerColor); + } + } + + // see if the cell to the right had a rowspan and its lower left border needs be joined with this one's bottom + if ((numCols != cellEndColIndex + 1) && // there is a cell to the right + (lastBottomBorders[cellEndColIndex + 1].span > 1)) { // cell to right was a rowspan + BCCornerInfo& corner = bottomCorners[cellEndColIndex + 1]; + if ((NS_SIDE_TOP != corner.ownerSide) && (NS_SIDE_BOTTOM != corner.ownerSide)) { // not a vertical owner + BCCellBorder& thisBorder = lastBottomBorder; + BCCellBorder& nextBorder = lastBottomBorders[info.colIndex + 1]; + if ((thisBorder.color == nextBorder.color) && (thisBorder.width == nextBorder.width) && + (thisBorder.style == nextBorder.style)) { + // set the flag on the next border indicating it is not the start of a new segment + if (iter.mCellMap) { + BCData* bcData = tableCellMap->GetBCData(NS_SIDE_BOTTOM, *iter.mCellMap, cellEndRowIndex, + cellEndColIndex + 1); + if (bcData) { + bcData->SetTopStart(PR_FALSE); + } + } + } + } + } + } // for (iter.First(info); info.cell; iter.Next(info)) { + + // reset the bc flag and damage area + SetNeedToCalcBCBorders(PR_FALSE); + propData->mDamageArea.x = propData->mDamageArea.y = propData->mDamageArea.width = propData->mDamageArea.height = 0; + //mCellMap->Dump(); +} + +// Iterates over borders (left border, corner, top border) in the cell map within a damage area +// from left to right, top to bottom. All members are in terms of the 1st in flow frames, except +// where suffixed by InFlow. +class BCMapBorderIterator +{ +public: + BCMapBorderIterator(nsTableFrame& aTableFrameInFlow, + nsTableRowGroupFrame& aRowGroupFrameInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea); + void Reset(nsTableFrame& aTableFrameInFlow, + nsTableRowGroupFrame& aRowGroupFrameInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea); + void First(); + void Next(); + + nsTableFrame* tableInFlow; + nsTableFrame* table; + nsTableCellMap* tableCellMap; + nsCellMap* cellMap; + + nsVoidArray rowGroups; + nsTableRowGroupFrame* prevRg; + nsTableRowGroupFrame* rgInFlow; + nsTableRowGroupFrame* rg; + PRInt32 rowGroupIndex; + PRInt32 rowGroupStart; + PRInt32 rowGroupEnd; + PRInt32 numRows; + + nsTableRowFrame* prevRow; + nsTableRowFrame* rowInFlow; + nsTableRowFrame* row; + PRInt32 numCols; + PRInt32 x; + PRInt32 y; + + nsTableCellFrame* prevCell; + nsTableCellFrame* cell; + BCCellData* prevCellData; + BCCellData* cellData; + BCData* bcData; + + PRBool IsTopMostTable() { return (y == 0) && !tableInFlow->GetPrevInFlow(); } + PRBool IsRightMostTable() { return (x >= numCols); } + PRBool IsBottomMostTable() { return (y >= numRows) && !tableInFlow->GetNextInFlow(); } + PRBool IsLeftMostTable() { return (x == 0); } + PRBool IsTopMost() { return (y == startY); } + PRBool IsRightMost() { return (x >= endX); } + PRBool IsBottomMost() { return (y >= endY); } + PRBool IsLeftMost() { return (x == startX); } + PRBool isNewRow; + + PRInt32 startX; + PRInt32 startY; + PRInt32 endX; + PRInt32 endY; + PRBool isRepeatedHeader; + PRBool isRepeatedFooter; + PRBool atEnd; + +private: + + PRBool SetNewRow(nsTableRowFrame* aRowInFlow = nsnull, + nsTableRowFrame* aRow = nsnull); + PRBool SetNewRowGroup(PRBool aSetInFlow); + void SetNewData(PRInt32 aY, PRInt32 aX); + +}; + +BCMapBorderIterator::BCMapBorderIterator(nsTableFrame& aTableInFlow, + nsTableRowGroupFrame& aRowGroupInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea) +{ + Reset(aTableInFlow, aRowGroupInFlow, aRowInFlow, aDamageArea); +} + +void +BCMapBorderIterator::Reset(nsTableFrame& aTableInFlow, + nsTableRowGroupFrame& aRowGroupInFlow, + nsTableRowFrame& aRowInFlow, + const nsRect& aDamageArea) +{ + atEnd = PR_TRUE; // gets reset when First() is called + + tableInFlow = &aTableInFlow; + table = (nsTableFrame*)aTableInFlow.GetFirstInFlow(); if (!table) ABORT0(); + rgInFlow = &aRowGroupInFlow; + rowInFlow = &aRowInFlow; + + tableCellMap = table->GetCellMap(); + + startX = aDamageArea.x; + startY = aDamageArea.y; + endY = aDamageArea.y + aDamageArea.height; + endX = aDamageArea.x + aDamageArea.width; + + numRows = table->GetRowCount(); + y = 0; + numCols = table->GetColCount(); + x = 0; + rowGroupIndex = -1; + rg = nsnull; + row = nsnull; + cell = nsnull; + cellData = nsnull; + + // Get the ordered row groups + PRUint32 numRowGroups; + table->OrderRowGroups(rowGroups, numRowGroups, nsnull); +} + +void +BCMapBorderIterator::SetNewData(PRInt32 aY, + PRInt32 aX) +{ + if (!tableCellMap || !tableCellMap->mBCInfo) ABORT0(); + + x = aX; + y = aY; + prevCellData = cellData; + if (IsRightMost() && IsBottomMost()) { + cell = nsnull; + bcData = &tableCellMap->mBCInfo->mLowerRightCorner; + } + else if (IsRightMost()) { + cellData = nsnull; + bcData = (BCData*)tableCellMap->mBCInfo->mRightBorders.ElementAt(aY); + } + else if (IsBottomMost()) { + cellData = nsnull; + bcData = (BCData*)tableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); + } + else { + bcData = nsnull; + nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(y - rowGroupStart); + if (row) { + cellData = (row->Count() > x) ? (BCCellData*)row->ElementAt(x) : nsnull; + if (cellData) { + bcData = &cellData->mData; + if (!cellData->IsOrig()) { + if (cellData->IsRowSpan()) { + aY -= cellData->GetRowSpanOffset(); + } + if (cellData->IsColSpan()) { + aX -= cellData->GetColSpanOffset(); + } + if ((aX >= 0) && (aY >= 0)) { + row = (nsVoidArray*)cellMap->mRows.ElementAt(aY - rowGroupStart); + if (row) { + cellData = (BCCellData*)row->ElementAt(aX); + } + } + } + if (cellData->IsOrig()) { + prevCell = cell; + cell = cellData->GetCellFrame(); + } + } + } + } +} + +PRBool +BCMapBorderIterator::SetNewRow(nsTableRowFrame* aRowInFlow, + nsTableRowFrame* aRow) +{ + prevRow = row; + row = (aRow) ? aRow : row->GetNextRow(); + rowInFlow = (aRowInFlow) ? aRowInFlow : rowInFlow->GetNextRow(); + + if (rowInFlow && row) { + isNewRow = PR_TRUE; + y = row->GetRowIndex(); + x = startX; + } + else { + atEnd = PR_TRUE; + } + return !atEnd; +} + + +PRBool +BCMapBorderIterator::SetNewRowGroup(PRBool aSetInFlow) +{ + rowGroupIndex++; + + isRepeatedHeader = PR_FALSE; + isRepeatedFooter = PR_FALSE; + + if (rowGroupIndex < rowGroups.Count()) { + prevRg = rg; + nsIFrame* frame = (nsTableRowGroupFrame*)rowGroups.ElementAt(rowGroupIndex); if (!frame) ABORT1(PR_FALSE); + rg = table->GetRowGroupFrame(frame); if (!rg) ABORT1(PR_FALSE); + rowGroupStart = rg->GetStartRowIndex(); + rowGroupEnd = rowGroupStart + rg->GetRowCount() - 1; + + if (aSetInFlow) { + if (rgInFlow && tableInFlow->GetPrevInFlow()) { + // if there is a prev in flow, then the next rgInFlow is (a) either the previous ones next sibling + // or (b) the rg of the next sibling of the parent if the parent is a scroll frame + nsIFrame* frame = nsnull; + nsIFrame* parent = nsnull; + rgInFlow->GetParent(&parent); + if (parent == tableInFlow) { + rgInFlow->GetNextSibling(&frame); + } + else if (parent) { + parent->GetNextSibling(&frame); + } + rgInFlow = table->GetRowGroupFrame(frame); + if (!rgInFlow) { + atEnd = PR_TRUE; + ABORT1(PR_FALSE); + } + } + else { + rgInFlow = rg; + } + } + if (SetNewRow(rgInFlow->GetFirstRow(), rg->GetFirstRow())) { + cellMap = tableCellMap->GetMapFor(*rg); + } + if (rgInFlow && tableInFlow->GetPrevInFlow() && !rgInFlow->GetPrevInFlow()) { + // if rgInFlow doesn't have a prev in flow, then it may be a repeated header or footer + const nsStyleDisplay* display; + rgInFlow->GetStyleData(eStyleStruct_Display, (const nsStyleStruct *&)display); + if (y == startY) { + isRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); + } + else { + isRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); + } + } + } + else { + atEnd = PR_TRUE; + } + return !atEnd; +} + +void +BCMapBorderIterator::First() +{ + if (!table || (startX >= numCols) || (startY >= numRows)) ABORT0(); + + atEnd = PR_FALSE; + + PRUint32 numRowGroups = rowGroups.Count(); + for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { + nsIFrame* frame = (nsIFrame*)rowGroups.ElementAt(rgX); + nsTableRowGroupFrame* rowG = table->GetRowGroupFrame(frame); + if (rowG) { + PRInt32 start = rowG->GetStartRowIndex(); + PRInt32 end = start + rowG->GetRowCount() - 1; + if ((startY >= start) && (startY <= end)) { + rowGroupIndex = rgX - 1; // SetNewRowGroup increments rowGroupIndex + if (SetNewRowGroup(PR_FALSE)) { + while ((y < startY) && !atEnd) { + SetNewRow(); + } + if (!atEnd) { + SetNewData(startY, startX); + } + } + return; + } + } + } + atEnd = PR_TRUE; +} + +void +BCMapBorderIterator::Next() +{ + if (atEnd) ABORT0(); + isNewRow = PR_FALSE; + + x++; + if (x > endX) { + y++; + if (y == endY) { + x = startX; + } + else if (y < endY) { + if (y <= rowGroupEnd) { + SetNewRow(); + } + else { + SetNewRowGroup(PR_TRUE); + } + } + else { + atEnd = PR_TRUE; + } + } + if (!atEnd) { + SetNewData(y, x); + } +} + +// XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine them +static nscoord +CalcVerCornerOffset(PRUint8 aCornerOwnerSide, + nscoord aCornerSubWidth, + nscoord aHorWidth, + PRBool aIsStartOfSeg, + PRBool aIsBevel, + float aPixelsToTwips) +{ + nscoord offset = 0; + nscoord smallHalf, largeHalf; + if ((NS_SIDE_TOP == aCornerOwnerSide) || (NS_SIDE_BOTTOM == aCornerOwnerSide)) { + DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf; + } + } + else { + DivideBCBorderSize(aHorWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; + } + } + return NSToCoordRound(aPixelsToTwips * (float)offset); +} + +static nscoord +CalcHorCornerOffset(PRUint8 aCornerOwnerSide, + nscoord aCornerSubWidth, + nscoord aVerWidth, + PRBool aIsStartOfSeg, + PRBool aIsBevel, + float aPixelsToTwips) +{ + nscoord offset = 0; + nscoord smallHalf, largeHalf; + if ((NS_SIDE_LEFT == aCornerOwnerSide) || (NS_SIDE_RIGHT == aCornerOwnerSide)) { + DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf; + } + } + else { + DivideBCBorderSize(aVerWidth, smallHalf, largeHalf); + if (aIsBevel) { + offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; + } + else { + offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; + } + } + return NSToCoordRound(aPixelsToTwips * (float)offset); +} + +struct BCVerticalSeg +{ + BCVerticalSeg::BCVerticalSeg(); + + void Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + nscoord aVerSegWidth, + nscoord aPrevHorSegHeight, + nscoord aHorSegHeight, + float aPixelsToTwips, + BCVerticalSeg* aVerInfoArray); + + union { + nsTableColFrame* col; + PRInt32 colWidth; + }; + PRInt32 colX; + nsTableCellFrame* ajaCell; + nsTableCellFrame* firstCell; // cell at the start of the segment + nsTableCellFrame* lastCell; // cell at the current end of the segment + PRInt32 segY; + PRInt32 segHeight; + PRInt16 segWidth; // width in pixels + PRUint8 owner; + PRUint8 bevelSide; + PRUint16 bevelOffset; +}; + +BCVerticalSeg::BCVerticalSeg() +{ + col = nsnull; firstCell = lastCell = ajaCell = nsnull; colX = segY = segHeight = 0; + segWidth = bevelOffset = 0; bevelSide = 0; owner = eCellOwner; +} + +void +BCVerticalSeg::Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + nscoord aVerSegWidth, + nscoord aPrevHorSegHeight, + nscoord aHorSegHeight, + float aPixelsToTwips, + BCVerticalSeg* aVerInfoArray) +{ + PRUint8 ownerSide = 0; + PRPackedBool bevel = PR_FALSE; + PRInt32 xAdj = aIter.x - aIter.startX; + + nscoord cornerSubWidth = (aIter.bcData) ? aIter.bcData->GetCorner(ownerSide, bevel) : 0; + PRBool topBevel = (aVerSegWidth > 0) ? bevel : PR_FALSE; + nscoord maxHorSegHeight = PR_MAX(aPrevHorSegHeight, aHorSegHeight); + nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, maxHorSegHeight, + PR_TRUE, topBevel, aPixelsToTwips); + + bevelOffset = (topBevel) ? maxHorSegHeight : 0; + bevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + segY += offset; + segHeight = -offset; + segWidth = aVerSegWidth; + owner = aBorderOwner; + firstCell = aIter.cell; + if (xAdj > 0) { + ajaCell = aVerInfoArray[xAdj - 1].lastCell; + } +} + +struct BCHorizontalSeg +{ + BCHorizontalSeg::BCHorizontalSeg(); + + void Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + PRUint8 aCornerOwnerSide, + nscoord aSubWidth, + PRBool aBevel, + nscoord aTopVerSegWidth, + nscoord aBottomVerSegWidth, + nscoord aHorSegHeight, + nsTableCellFrame* aLastCell, + float aPixelsToTwips); + + nscoord x; + nscoord y; + nscoord width; + nscoord height; + PRBool leftBevel; + nscoord leftBevelOffset; + PRUint8 leftBevelSide; + PRUint8 owner; + nsTableCellFrame* firstCell; // cell at the start of the segment + nsTableCellFrame* ajaCell; +}; + +BCHorizontalSeg::BCHorizontalSeg() +{ + x = y = width = height = leftBevel = leftBevelOffset = leftBevelSide = 0; + firstCell = ajaCell = nsnull; +} + + +void +BCHorizontalSeg::Start(BCMapBorderIterator& aIter, + BCBorderOwner aBorderOwner, + PRUint8 aCornerOwnerSide, + nscoord aSubWidth, + PRBool aBevel, + nscoord aTopVerSegWidth, + nscoord aBottomVerSegWidth, + nscoord aHorSegHeight, + nsTableCellFrame* aLastCell, + float aPixelsToTwips) +{ + owner = aBorderOwner; + leftBevel = (aHorSegHeight > 0) ? aBevel : PR_FALSE; + nscoord maxVerSegWidth = PR_MAX(aTopVerSegWidth, aBottomVerSegWidth); + nscoord offset = CalcHorCornerOffset(aCornerOwnerSide, aSubWidth, maxVerSegWidth, + PR_TRUE, leftBevel, aPixelsToTwips); + leftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; + leftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + x += offset; + width = -offset; + height = aHorSegHeight; + firstCell = aIter.cell; + ajaCell = (aIter.IsTopMost()) ? nsnull : aLastCell; +} + +void +nsTableFrame::PaintBCBorders(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + nsMargin childAreaOffset = GetChildAreaOffset(*aPresContext, nsnull); + nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT0(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + + PRInt32 startColX = childAreaOffset.left; // x position of first col in damage area + PRInt32 startRowY = (mPrevInFlow) ? 0 : childAreaOffset.top; // y position of first row in damage area + + const nsStyleBackground* bgColor = nsStyleUtil::FindNonTransparentBackground(mStyleContext); + // determine the damage area in terms of rows and columns and finalize startColX and startRowY + PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex; + startRowIndex = endRowIndex = startColIndex = endColIndex = 0; + + nsAutoVoidArray rowGroups; + PRUint32 numRowGroups; + OrderRowGroups(rowGroups, numRowGroups, nsnull); + PRBool done = PR_FALSE; + PRBool haveIntersect = PR_FALSE; + nsTableRowGroupFrame* inFlowRG = nsnull; + nsTableRowFrame* inFlowRow = nsnull; + // find startRowIndex, endRowIndex, startRowY + nscoord onePixel = NSToCoordRound(p2t); + PRInt32 rowY = startRowY; + for (PRUint32 rgX = 0; (rgX < numRowGroups) && !done; rgX++) { + nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX); + nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0(); + nsRect rowRect; + for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { + // conservatively estimate the half border widths outside the row + nscoord topBorderHalf = (mPrevInFlow) ? 0 : rowFrame->GetTopBCBorderWidth(&p2t) + onePixel; + nscoord bottomBorderHalf = (mNextInFlow) ? 0 : rowFrame->GetBottomBCBorderWidth(&p2t) + onePixel; + // get the row rect relative to the table rather than the row group + rowFrame->GetRect(rowRect); + if (haveIntersect) { + if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { + nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0(); + endRowIndex = fifRow->GetRowIndex(); + } + else done = PR_TRUE; + } + else { + if ((rowY + rowRect.height + bottomBorderHalf) >= aDirtyRect.y) { + inFlowRG = rgFrame; + inFlowRow = rowFrame; + nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0(); + startRowIndex = endRowIndex = fifRow->GetRowIndex(); + haveIntersect = PR_TRUE; + } + else { + startRowY += rowRect.height; + } + } + rowY += rowRect.height; + } + } + if (!inFlowRG || !inFlowRow) ABORT0(); + + // find startColIndex, endColIndex, startColX + haveIntersect = PR_FALSE; + PRUint32 numCols = GetColCount(); + if (0 == numCols) return; + + nscoord x = 0; + for (PRUint32 colX = 0; colX < numCols; colX++) { + nsTableColFrame* colFrame = firstInFlow->GetColFrame(colX); if (!colFrame) ABORT0(); + // conservatively estimate the half border widths outside the col + nscoord leftBorderHalf = colFrame->GetLeftBorderWidth(&p2t) + onePixel; + nscoord rightBorderHalf = colFrame->GetRightBorderWidth(&p2t) + onePixel; + // get the col rect relative to the table rather than the col group + nsRect rect; + colFrame->GetRect(rect); + if (haveIntersect) { + if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { + endColIndex = colX; + } + else break; + } + else { + if ((x + rect.width + rightBorderHalf) >= aDirtyRect.x) { + startColIndex = endColIndex = colX; + haveIntersect = PR_TRUE; + } + else { + startColX += rect.width; + } + } + x += rect.width; + } + + // iterate the cell map and build up border segments + nsRect damageArea(startColIndex, startRowIndex, 1 + endColIndex - startColIndex, + 1 + endRowIndex - startRowIndex); + BCVerticalSeg* verInfo = new BCVerticalSeg[damageArea.width + 1]; if (!verInfo) ABORT0(); + + BCBorderOwner borderOwner, ignoreBorderOwner; + PRUint8 ownerSide; + nscoord cornerSubWidth, smallHalf, largeHalf; + nsRect rowRect(0,0,0,0); + PRBool isSegStart, ignoreSegStart; + nscoord prevHorSegHeight = 0; + PRPackedBool bevel; + PRInt32 repeatedHeaderY = -99; + PRBool afterRepeatedHeader = PR_FALSE; + PRBool startRepeatedFooter = PR_FALSE; + + // First, paint all of the vertical borders from top to bottom and left to right as they become complete + // They are painted first, since they are less efficient to paint than horizontal segments. They were + // stored with as few segments as possible (since horizontal borders are painted last and possibly over them). + BCMapBorderIterator iter(*this, *inFlowRG, *inFlowRow, damageArea); + for (iter.First(); !iter.atEnd; iter.Next()) { + nscoord verSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(borderOwner, isSegStart) : 0; + nscoord horSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0; + + PRInt32 xAdj = iter.x - iter.startX; + if (iter.isNewRow) { + prevHorSegHeight = 0; + iter.rowInFlow->GetRect(rowRect); + if (iter.isRepeatedHeader) { + repeatedHeaderY = iter.y; + } + afterRepeatedHeader = !iter.isRepeatedHeader && (iter.y == (repeatedHeaderY + 1)); + startRepeatedFooter = iter.isRepeatedFooter && (iter.y == iter.rowGroupStart) && (iter.y != iter.startY); + } + BCVerticalSeg& info = verInfo[xAdj]; + if (!info.col) { // on the first damaged row and the first segment in the col + info.col = iter.IsRightMostTable() ? verInfo[xAdj - 1].col : firstInFlow->GetColFrame(iter.x); if (!info.col) ABORT0(); + if (0 == xAdj) { + info.colX = startColX; + } + // set colX for the next column + if (!iter.IsRightMost()) { + nsRect colRect; + info.col->GetRect(colRect); + verInfo[xAdj + 1].colX = info.colX + colRect.width; + } + info.segY = startRowY; + info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, p2t, verInfo); + info.lastCell = iter.cell; + } + + if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost() || afterRepeatedHeader || startRepeatedFooter)) { + // paint the previous seg or the current one if iter.IsBottomMost() + if (info.segHeight > 0) { + cornerSubWidth = (iter.bcData) ? iter.bcData->GetCorner(ownerSide, bevel) : 0; + PRBool endBevel = (info.segWidth > 0) ? bevel : PR_FALSE; + nscoord bottomHorSegHeight = PR_MAX(prevHorSegHeight, horSegHeight); + nscoord endOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, bottomHorSegHeight, + PR_FALSE, endBevel, p2t); + info.segHeight += endOffset; + if (info.segWidth > 0) { + // get the border style, color and paint the segment + PRUint8 side = (iter.IsRightMost()) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + nsTableColGroupFrame* cg = nsnull; + nsTableColFrame* col = info.col; if (!col) ABORT0(); + nsTableCellFrame* cell = info.firstCell; + PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; + nscolor color = 0xFFFFFFFF; + PRBool ignoreIfRules = (iter.IsRightMostTable() || iter.IsLeftMostTable()); + + switch (info.owner) { + case eTableOwner: + ::GetStyleInfo(*this, side, style, color); + break; + case eAjaColGroupOwner: + side = NS_SIDE_RIGHT; + if (!iter.IsRightMostTable() && (xAdj > 0)) { + col = verInfo[xAdj - 1].col; + } // and fall through + case eColGroupOwner: + if (col) { + col->GetParent((nsIFrame**)&cg); + if (cg) { + ::GetStyleInfo(*cg, side, style, color, ignoreIfRules); + } + } + break; + case eAjaColOwner: + side = NS_SIDE_RIGHT; + if (!iter.IsRightMostTable() && (xAdj > 0)) { + col = verInfo[xAdj - 1].col; + } // and fall through + case eColOwner: + if (col) { + ::GetStyleInfo(*col, side, style, color, ignoreIfRules); + } + break; + case eAjaRowGroupOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eRowGroupOwner: + NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error"); + if (iter.rg) { + ::GetStyleInfo(*iter.rg, side, style, color, ignoreIfRules); + } + break; + case eAjaRowOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eRowOwner: + NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error"); + if (iter.row) { + ::GetStyleInfo(*iter.row, side, style, color, ignoreIfRules); + } + break; + case eAjaCellOwner: + side = NS_SIDE_RIGHT; + cell = info.ajaCell; // and fall through + case eCellOwner: + if (cell) { + ::GetStyleInfo(*cell, side, style, color); + } + break; + } + DivideBCBorderSize(info.segWidth, smallHalf, largeHalf); + nsRect segRect(info.colX - NSToCoordRound(p2t * (float)largeHalf), info.segY, + NSToCoordRound(p2t * (float)info.segWidth), info.segHeight); + nscoord bottomBevelOffset = (endBevel) ? NSToCoordRound(p2t * (float)bottomHorSegHeight) : 0; + PRUint8 bottomBevelSide = (horSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, p2t, + info.bevelSide, NSToCoordRound(p2t * (float)info.bevelOffset), + bottomBevelSide, bottomBevelOffset); + } // if (info.segWidth > 0) { + info.segY = info.segY + info.segHeight - endOffset; + } // if (info.segHeight > 0) + info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, p2t, verInfo); + } // if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost())) { + + info.lastCell = iter.cell; + info.segHeight += rowRect.height; + prevHorSegHeight = horSegHeight; + } // for (iter.First(); !iter.atEnd; iter.Next()) { + + // Next, paint all of the horizontal border segments from top to bottom reuse the verInfo + // array to keep tract of col widths and vertical segments for corner calculations + memset(verInfo, 0, damageArea.width * sizeof(BCVerticalSeg)); // XXX reinitialize properly + for (PRInt32 xIndex = 0; xIndex < damageArea.width; xIndex++) { + verInfo[xIndex].colWidth = -1; + } + PRInt32 nextY = startRowY; + BCHorizontalSeg horSeg; + + iter.Reset(*this, *inFlowRG, *inFlowRow, damageArea); + for (iter.First(); !iter.atEnd; iter.Next()) { + nscoord leftSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0; + nscoord topSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(borderOwner, isSegStart) : 0; + + PRInt32 xAdj = iter.x - iter.startX; + // store the current col width if it hasn't been already + if (verInfo[xAdj].colWidth < 0) { + if (iter.IsRightMostTable()) { + verInfo[xAdj].colWidth = verInfo[xAdj - 1].colWidth; + } + else { + nsTableColFrame* col = firstInFlow->GetColFrame(iter.x); if (!col) ABORT0(); + nsRect colRect; + col->GetRect(colRect); + verInfo[xAdj].colWidth = colRect.width; + } + } + cornerSubWidth = (iter.bcData) ? iter.bcData->GetCorner(ownerSide, bevel) : 0; + nscoord verWidth = PR_MAX(verInfo[xAdj].segWidth, leftSegWidth); + if (iter.isNewRow || (iter.IsLeftMost() && iter.IsBottomMostTable())) { + iter.rowInFlow->GetRect(rowRect); + horSeg.y = nextY; + nextY = nextY + rowRect.height; + horSeg.x = startColX; + horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth, + leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, p2t); + } + PRBool verOwnsCorner = (NS_SIDE_TOP == ownerSide) || (NS_SIDE_BOTTOM == ownerSide); + if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) { + // paint the previous seg or the current one if iter.IsRightMost() + if (horSeg.width > 0) { + PRBool endBevel = (horSeg.height > 0) ? bevel : 0; + nscoord endOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, PR_FALSE, endBevel, p2t); + horSeg.width += endOffset; + if (horSeg.height > 0) { + // get the border style, color and paint the segment + PRUint8 side = (iter.IsBottomMost()) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + nsIFrame* rg = iter.rg; if (!rg) ABORT0(); + nsIFrame* row = iter.row; if (!row) ABORT0(); + nsIFrame* cell = horSeg.firstCell; if (!cell) ABORT0(); + nsIFrame* cg; + nsIFrame* col; + + PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID; + nscolor color = 0xFFFFFFFF; + PRBool ignoreIfRules = (iter.IsTopMostTable() || iter.IsBottomMostTable()); + + switch (horSeg.owner) { + case eTableOwner: + ::GetStyleInfo(*this, side, style, color); + break; + case eAjaColGroupOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eColGroupOwner: + NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error"); + col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0(); + col->GetParent(&cg); if (!cg) ABORT0(); + ::GetStyleInfo(*cg, side, style, color, ignoreIfRules); + break; + case eAjaColOwner: + NS_ASSERTION(PR_FALSE, "program error"); // and fall through + case eColOwner: + NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error"); + col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0(); + ::GetStyleInfo(*col, side, style, color, ignoreIfRules); + break; + case eAjaRowGroupOwner: + side = NS_SIDE_BOTTOM; + rg = (iter.IsBottomMostTable()) ? iter.rg : iter.prevRg; // and fall through + case eRowGroupOwner: + if (rg) { + ::GetStyleInfo(*rg, side, style, color, ignoreIfRules); + } + break; + case eAjaRowOwner: + side = NS_SIDE_BOTTOM; + row = iter.prevRow; // and fall through + case eRowOwner: + if (row) { + ::GetStyleInfo(*row, side, style, color, iter.IsBottomMostTable()); + } + break; + case eAjaCellOwner: + side = NS_SIDE_BOTTOM; + // if this is null due to the damage area origin-y > 0, then the border won't show up anyway + cell = horSeg.ajaCell; + // and fall through + case eCellOwner: + if (cell) { + ::GetStyleInfo(*cell, side, style, color); + } + break; + } + DivideBCBorderSize(horSeg.height, smallHalf, largeHalf); + nsRect segRec(horSeg.x, horSeg.y - NSToCoordRound(p2t * (float)largeHalf), horSeg.width, + NSToCoordRound(p2t * (float)horSeg.height)); + nscoord rightBevelOffset = (endBevel) ? NSToCoordRound(p2t * (float)verWidth) : 0; + PRUint8 rightBevelSide = (leftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; + nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRec, p2t, horSeg.leftBevelSide, + NSToCoordRound(p2t * (float)horSeg.leftBevelOffset), + rightBevelSide, rightBevelOffset); + } // if (horSeg.height > 0) { + horSeg.x = horSeg.x + horSeg.width - endOffset; + } // if (horSeg.width > 0) { + horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth, + leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, p2t); + } // if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) { + horSeg.width += verInfo[xAdj].colWidth; + verInfo[xAdj].segWidth = leftSegWidth; + verInfo[xAdj].lastCell = iter.cell; + } + delete [] verInfo; +} /******************************************************************************** ** DEBUG_TABLE_REFLOW and DEBUG_TABLE_REFLOW_TIMING ** @@ -4579,7 +6993,7 @@ GetFrameTypeName(nsIAtom* aFrameType, strcpy(aName, "RowG"); else if (nsLayoutAtoms::tableRowFrame == aFrameType) strcpy(aName, "Row"); - else if (nsLayoutAtoms::tableCellFrame == aFrameType) + else if (IS_TABLE_CELL(aFrameType)) strcpy(aName, "Cell"); else if (nsLayoutAtoms::blockFrame == aFrameType) strcpy(aName, "Block"); @@ -4621,7 +7035,7 @@ void DebugGetIndent(const nsIFrame* aFrame, (nsLayoutAtoms::tableFrame == frameType.get()) || (nsLayoutAtoms::tableRowGroupFrame == frameType.get()) || (nsLayoutAtoms::tableRowFrame == frameType.get()) || - (nsLayoutAtoms::tableCellFrame == frameType.get())) { + IS_TABLE_CELL(frameType.get())) { numLevels++; } if (nsLayoutAtoms::blockFrame == frameType.get()) { @@ -4630,7 +7044,7 @@ void DebugGetIndent(const nsIFrame* aFrame, parent->GetParent(&grandParent); nsCOMPtr gFrameType; grandParent->GetFrameType(getter_AddRefs(gFrameType)); - if (nsLayoutAtoms::tableCellFrame == gFrameType.get()) { + if (IS_TABLE_CELL(gFrameType.get())) { numLevels++; } } @@ -4715,14 +7129,14 @@ nsReflowTimer* GetFrameTimer(nsIFrame* aFrame, return ((nsTableRowGroupFrame*)aFrame)->mTimer; else if (nsLayoutAtoms::tableRowFrame == aFrameType) return ((nsTableRowFrame*)aFrame)->mTimer; - else if (nsLayoutAtoms::tableCellFrame == aFrameType) + else if (IS_TABLE_CELL(aFrameType)) return ((nsTableCellFrame*)aFrame)->mTimer; else if (nsLayoutAtoms::blockFrame == aFrameType) { nsIFrame* parentFrame; aFrame->GetParent(&parentFrame); nsCOMPtr fType; parentFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType) { + if (IS_TABLE_CELL(fType)) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)parentFrame; // fix up the block timer, which may be referring to the cell if (cellFrame->mBlockTimer->mFrame == parentFrame) { @@ -4967,7 +7381,7 @@ void nsTableFrame::DebugReflowDone(nsIFrame* aFrame) ancestorTimer->mChildren.AppendElement(thisTimer); nsCOMPtr fType; aFrame->GetFrameType(getter_AddRefs(fType)); - if (nsLayoutAtoms::tableCellFrame == fType) { + if (IS_TABLE_CELL(fType)) { // add the cell block timer as a child of the cell timer nsTableCellFrame* cellFrame = (nsTableCellFrame*)aFrame; cellFrame->mTimer->mChildren.AppendElement(cellFrame->mBlockTimer); @@ -5056,6 +7470,16 @@ DestroyPointFunc(nsIPresContext* aPresContext, delete (nsPoint*)aPropertyValue; } +// Destructor function for nscoord properties +static void +DestroyBCPropertyDataFunc(nsIPresContext* aPresContext, + nsIFrame* aFrame, + nsIAtom* aPropertyName, + void* aPropertyValue) +{ + delete (BCPropertyData*)aPropertyValue; +} + void* nsTableFrame::GetProperty(nsIPresContext* aPresContext, nsIFrame* aFrame, @@ -5089,6 +7513,10 @@ nsTableFrame::GetProperty(nsIPresContext* aPresContext, value = new nscoord; dtorFunc = DestroyCoordFunc; } + else if (aPropertyName == nsLayoutAtoms::tableBCProperty) { + value = new BCPropertyData; + dtorFunc = DestroyBCPropertyDataFunc; + } if (!value) return nsnull; frameManager->SetFrameProperty(aFrame, aPropertyName, value, dtorFunc); @@ -5160,7 +7588,7 @@ void DumpTableFramesRecur(nsIPresContext* aPresContext, if (nsLayoutAtoms::tableFrame == fType.get() || nsLayoutAtoms::tableRowGroupFrame == fType.get() || nsLayoutAtoms::tableRowFrame == fType.get() || - nsLayoutAtoms::tableCellFrame == fType.get()) { + IS_TABLE_CELL(fType.get())) { nsIFrame* child; aFrame->FirstChild(aPresContext, nsnull, &child); while(child) { diff --git a/mozilla/layout/tables/nsTableFrame.h b/mozilla/layout/tables/nsTableFrame.h index 4862e4a92c2..7dc64be49b4 100644 --- a/mozilla/layout/tables/nsTableFrame.h +++ b/mozilla/layout/tables/nsTableFrame.h @@ -53,7 +53,6 @@ class nsTableColFrame; class nsTableRowGroupFrame; class nsTableRowFrame; class nsTableColGroupFrame; -class nsTableBorderCollapser; class nsITableLayoutStrategy; class nsHTMLValue; @@ -208,6 +207,8 @@ public: nsIAtom* aPropertyName, PRBool aCreateIfNecessary = PR_FALSE); + static float GetTwipsToPixels(nsIPresContext* aPresContext); + static nscoord RoundToPixel(nscoord aValue, float aPixelToTwips, nsPixelRound aRound= eAlwaysRoundUp); @@ -249,7 +250,13 @@ public: nsIAtom* aListName, nsIFrame* aOldFrame); - nsMargin GetBorderPadding(const nsHTMLReflowState& aReflowState) const; + // Get the offset from the border box to the area where the row groups fit + nsMargin GetChildAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const; + + // Get the offset from the border box to the area where the content fits + nsMargin GetContentAreaOffset(nsIPresContext& aPresContext, + const nsHTMLReflowState* aReflowState) const; /** helper method to find the table parent of any table frame object */ // TODO: today, this depends on display types. This should be changed to rely @@ -297,6 +304,17 @@ public: nsFramePaintLayer aWhichLayer, PRUint32 aFlags = 0); + nsMargin* GetBCBorder(nsIPresContext& aPresContext, + PRBool aInnerBorderOnly, + nsMargin& aBorder) const; + + void SetBCDamageArea(nsIPresContext& aPresContext, + const nsRect& aValue); + + void PaintBCBorders(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD GetFrameForPoint(nsIPresContext* aPresContext, const nsPoint& aPoint, nsFramePaintLayer aWhichLayer, @@ -371,15 +389,12 @@ public: /** set the width of the column at aColIndex to aWidth */ virtual void SetColumnWidth(PRInt32 aColIndex, nscoord aWidth); - /** helper to get the border collapse style value */ - virtual PRUint8 GetBorderCollapseStyle(); - /** helper to get the cell spacing X style value */ virtual nscoord GetCellSpacingX(); /** helper to get the cell spacing Y style value */ virtual nscoord GetCellSpacingY(); - + /** return the row span of a cell, taking into account row span magic at the bottom * of a table. The row span equals the number of rows spanned by aCell starting at * aStartRowIndex, and can be smaller if aStartRowIndex is greater than the row @@ -440,9 +455,9 @@ public: /** empty the column frame cache */ void ClearColCache(); - virtual PRInt32 AppendCell(nsIPresContext& aPresContext, - nsTableCellFrame& aCellFrame, - PRInt32 aRowIndex); + virtual void AppendCell(nsIPresContext& aPresContext, + nsTableCellFrame& aCellFrame, + PRInt32 aRowIndex); virtual void InsertCells(nsIPresContext& aPresContext, nsVoidArray& aCellFrames, @@ -522,6 +537,9 @@ protected: /** destructor, responsible for mColumnLayoutData */ virtual ~nsTableFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState); + /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -619,7 +637,8 @@ protected: nsIFrame* aKidFrame, nscoord aDeltaY); - nsresult RecoverState(nsTableReflowState& aReflowState, + nsresult RecoverState(nsIPresContext& aPresContext, + nsTableReflowState& aReflowState, nsIFrame* aKidFrame); NS_METHOD CollapseRowGroupIfNecessary(nsIPresContext* aPresContext, @@ -646,10 +665,11 @@ public: // calculate the computed height of aFrame including its border and padding given // its reflow state. - nscoord CalcBorderBoxHeight(const nsHTMLReflowState& aReflowState); + nscoord CalcBorderBoxHeight(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState); // calculate the minimum width to layout aFrame and its desired width // including border and padding given its reflow state and column width information - void CalcMinAndPreferredWidths(nsIPresContext* aPresContextconst, + void CalcMinAndPreferredWidths(nsIPresContext* aPresContext, const nsHTMLReflowState& aReflowState, PRBool aCalcPrefWidthIfAutoWithPctCol, nscoord& aMinWidth, @@ -657,7 +677,8 @@ public: protected: // calcs the width of the table according to the computed widths of each column. - virtual PRInt32 CalcDesiredWidth(const nsHTMLReflowState& aReflowState); + virtual PRInt32 CalcDesiredWidth(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState); // return the desired height of this table accounting for the current // reflow state, and for the table attributes and parent @@ -699,7 +720,7 @@ public: PRUint32& aNumRowGroups, nsIFrame** aFirstBody = nsnull, nsTableRowGroupFrame** aHead = nsnull, - nsTableRowGroupFrame** aFoot = nsnull); + nsTableRowGroupFrame** aFoot = nsnull) const; // Returns PR_TRUE if there are any cells above the row at // aRowIndex and spanning into the row at aRowIndex @@ -735,9 +756,6 @@ protected: PRBool DidResizeReflow() const; void SetResizeReflow(PRBool aValue); - /** Support methods for DidSetStyleContext */ - void MapBorderMarginPadding(nsIPresContext* aPresContext); - void MapHTMLBorderStyle(nsStyleBorder& aBorderStyle, nscoord aBorderWidth); PRBool ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult); @@ -749,6 +767,11 @@ public: PRBool NeedStrategyBalance() const; void SetNeedStrategyBalance(PRBool aValue); + PRBool IsBorderCollapse() const; + + PRBool NeedToCalcBCBorders() const; + void SetNeedToCalcBCBorders(PRBool aValue); + /** Get the cell map for this table frame. It is not always mCellMap. * Only the firstInFlow has a legit cell map */ @@ -763,9 +786,6 @@ public: PRInt32 aRowIndex, PRInt32 anAdjustment); - // Return PR_TRUE if rules=groups is set for the table content - PRBool HasGroupRules() const; - // Remove cell borders which aren't bordering row and/or col groups void ProcessGroupRules(nsIPresContext* aPresContext); @@ -775,10 +795,16 @@ public: * Return aFrame's child if aFrame is an nsScrollFrame, otherwise return aFrame */ nsTableRowGroupFrame* GetRowGroupFrame(nsIFrame* aFrame, - nsIAtom* aFrameTypeIn = nsnull); + nsIAtom* aFrameTypeIn = nsnull) const; protected: + void SetBorderCollapse(PRBool aValue); + + void CalcBCBorders(nsIPresContext& aPresContext); + + void ExpandBCDamageArea(nsRect& aRect) const; + PRBool HadInitialReflow() const; void SetHadInitialReflow(PRBool aValue); @@ -809,8 +835,8 @@ public: /* ----- Cell Map public methods ----- */ /** returns the number of columns in this table after redundant columns have been removed */ - virtual PRInt32 GetEffectiveColCount(); - virtual PRInt32 GetColCount(); + virtual PRInt32 GetEffectiveColCount() const; + virtual PRInt32 GetColCount() const; /** return the column frame at colIndex. * returns nsnull if the col frame has not yet been allocated, or if aColIndex is out of range @@ -886,6 +912,7 @@ protected: unsigned mHasPctCol:1; // does any cell or col have a pct width unsigned mCellSpansPctCol:1; // does any cell span a col with a pct width (or containing a cell with a pct width) unsigned mDidResizeReflow:1; // did a resize reflow happen (indicating pass 2) + unsigned mIsBorderCollapse:1; // border collapsing model vs. separate model // true if a descendant was reflowed normally since the last time we reflowed. // We will likely need a timeout reflow (targeted either at us or below) unsigned mDescendantReflowedNotTimeout:1; @@ -896,7 +923,8 @@ protected: unsigned mRowInserted:1; unsigned mNeedSpecialReflow:1; unsigned mNeedToInitiateSpecialReflow:1; - unsigned : 20; // unused + unsigned mNeedToCalcBCBorders:1; + unsigned : 18; // unused } mBits; nsTableCellMap* mCellMap; // maintains the relationships between rows, cols, and cells @@ -1054,8 +1082,26 @@ inline void nsTableFrame::SetPreferredWidth(nscoord aWidth) mPreferredWidth = aWidth; } +inline PRBool nsTableFrame::IsBorderCollapse() const +{ + return (PRBool)mBits.mIsBorderCollapse; +} + +inline void nsTableFrame::SetBorderCollapse(PRBool aValue) +{ + mBits.mIsBorderCollapse = aValue; +} + +inline PRBool nsTableFrame::NeedToCalcBCBorders() const +{ + return (PRBool)mBits.mNeedToCalcBCBorders; +} + +inline void nsTableFrame::SetNeedToCalcBCBorders(PRBool aValue) +{ + mBits.mNeedToCalcBCBorders = (unsigned)aValue; +} - enum nsTableIteration { eTableLTR = 0, eTableRTL = 1, @@ -1085,6 +1131,23 @@ protected: PRInt32 mCount; }; +#define ABORT0() \ +{NS_ASSERTION(PR_FALSE, "CellIterator program error"); \ +return;} + +#define ABORT1(aReturn) \ +{NS_ASSERTION(PR_FALSE, "CellIterator program error"); \ +return aReturn;} + +#define GET_PIXELS_TO_TWIPS(presContext,var) \ + float var; \ + (presContext)->GetScaledPixelsToTwips(&var); + +#define GET_TWIPS_TO_PIXELS(presContext,var) \ + float var; \ + (presContext)->GetScaledPixelsToTwips(&var); \ + var = 1.0f / var; + #endif diff --git a/mozilla/layout/tables/nsTableOuterFrame.cpp b/mozilla/layout/tables/nsTableOuterFrame.cpp index cd77fdd51e4..d402caa1396 100644 --- a/mozilla/layout/tables/nsTableOuterFrame.cpp +++ b/mozilla/layout/tables/nsTableOuterFrame.cpp @@ -232,7 +232,14 @@ nsTableOuterFrame::SetInitialChildList(nsIPresContext* aPresContext, } else { mFrames.SetFrames(aChildList); - mInnerTableFrame = aChildList; + mInnerTableFrame = nsnull; + if (aChildList) { + nsCOMPtr fType; + aChildList->GetFrameType(getter_AddRefs(fType)); + if (nsLayoutAtoms::tableFrame == fType.get()) { + mInnerTableFrame = (nsTableFrame*)aChildList; + } + } } return NS_OK; @@ -466,21 +473,46 @@ FixAutoMargins(nscoord aAvailWidth, } } +void +nsTableOuterFrame::InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState) + +{ + nsMargin collapseBorder; + nsMargin collapsePadding(0,0,0,0); + nsMargin* pCollapseBorder = nsnull; + nsMargin* pCollapsePadding = nsnull; + if ((aReflowState.frame == mInnerTableFrame) && (mInnerTableFrame->IsBorderCollapse())) { + if (mInnerTableFrame->NeedToCalcBCBorders()) { + mInnerTableFrame->CalcBCBorders(aPresContext); + } + pCollapseBorder = mInnerTableFrame->GetBCBorder(aPresContext, PR_FALSE, collapseBorder); + pCollapsePadding = &collapsePadding; + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding); +} + // get the margin and padding data. nsHTMLReflowState doesn't handle the // case of auto margins void -GetMarginPadding(nsIPresContext* aPresContext, - const nsHTMLReflowState& aOuterRS, - nsIFrame* aChildFrame, - nsMargin& aMargin, - nsMargin& aMarginNoAuto, - nsMargin& aPadding) +nsTableOuterFrame::GetMarginPadding(nsIPresContext* aPresContext, + const nsHTMLReflowState& aOuterRS, + nsIFrame* aChildFrame, + nsMargin& aMargin, + nsMargin& aMarginNoAuto, + nsMargin& aPadding) { + if (!aPresContext) ABORT0(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); // construct a reflow state to compute margin and padding. Auto margins // will not be computed at this time. + + // create an init the child reflow state nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, nsSize(aOuterRS.availableWidth, aOuterRS.availableHeight), - eReflowReason_Resize); + eReflowReason_Resize, PR_FALSE); + InitChildReflowState(*aPresContext, childRS); + nsRect childRect; aChildFrame->GetRect(childRect); FixAutoMargins(aOuterRS.availableWidth, childRect.width, childRS); @@ -931,6 +963,7 @@ nsTableOuterFrame::OuterReflowChild(nsIPresContext* aPresContext, nsReflowReason aReflowReason, nsReflowStatus& aStatus) { + if (!aPresContext) ABORT1(NS_ERROR_NULL_POINTER); aMargin = aPadding = nsMargin(0,0,0,0); nscoord availWidth = GetChildAvailWidth(aPresContext, aChildFrame, aOuterRS, @@ -946,9 +979,10 @@ nsTableOuterFrame::OuterReflowChild(nsIPresContext* aPresContext, availHeight = nsTableFrame::RoundToPixel(availHeight, p2t, eAlwaysRoundDown); } - nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, - nsSize(availWidth, availHeight)); - childRS.reason = aReflowReason; + // create and init the child reflow state + nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, nsSize(availWidth, availHeight), + aReflowReason); + InitChildReflowState(*aPresContext, childRS); childRS.mPercentHeightObserver = nsnull; // the observer is for non table related frames inside cells diff --git a/mozilla/layout/tables/nsTableOuterFrame.h b/mozilla/layout/tables/nsTableOuterFrame.h index ee689ab6f08..16eaba4c89d 100644 --- a/mozilla/layout/tables/nsTableOuterFrame.h +++ b/mozilla/layout/tables/nsTableOuterFrame.h @@ -47,6 +47,7 @@ class nsReflowTimer; #endif struct nsStyleTable; +class nsTableFrame; class nsTableCaptionFrame : public nsBlockFrame { @@ -201,6 +202,9 @@ protected: nsTableOuterFrame(); virtual ~nsTableOuterFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + nsHTMLReflowState& aReflowState); + /** Always returns 0, since the outer table frame has no border of its own * The inner table frame can answer this question in a meaningful way. * @see nsHTMLContainerFrame::GetSkipSides */ @@ -362,11 +366,17 @@ protected: PRBool aInnerChanged, PRBool aCaptionChanged); + void GetMarginPadding(nsIPresContext* aPresContext, + const nsHTMLReflowState& aOuterRS, + nsIFrame* aChildFrame, + nsMargin& aMargin, + nsMargin& aMarginNoAuto, + nsMargin& aPadding); private: // used to keep track of this frame's children. They are redundant with mFrames, but more convient - nsIFrame* mInnerTableFrame; - nsIFrame* mCaptionFrame; + nsTableFrame* mInnerTableFrame; + nsIFrame* mCaptionFrame; // used to track caption max element size PRInt32 mMinCaptionWidth; diff --git a/mozilla/layout/tables/nsTableRowFrame.cpp b/mozilla/layout/tables/nsTableRowFrame.cpp index 59055548c3f..48687f0efb5 100644 --- a/mozilla/layout/tables/nsTableRowFrame.cpp +++ b/mozilla/layout/tables/nsTableRowFrame.cpp @@ -68,11 +68,6 @@ struct nsTableCellReflowState : public nsHTMLReflowState const nsSize& aAvailableSpace, nsReflowReason aReason); - nsTableCellReflowState(nsIPresContext* aPresContext, - const nsHTMLReflowState& aParentReflowState, - nsIFrame* aFrame, - const nsSize& aAvailableSpace); - void FixUp(const nsSize& aAvailSpace); }; @@ -83,16 +78,43 @@ nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresCon nsReflowReason aReason) :nsHTMLReflowState(aPresContext, aParentRS, aFrame, aAvailSpace, aReason) { - FixUp(aAvailSpace); } -nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresContext, - const nsHTMLReflowState& aParentRS, - nsIFrame* aFrame, - const nsSize& aAvailSpace) - :nsHTMLReflowState(aPresContext, aParentRS, aFrame, aAvailSpace) +void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) { - FixUp(aAvailSpace); + // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base + if (NS_UNCONSTRAINEDSIZE != aAvailSpace.width) { + if (NS_UNCONSTRAINEDSIZE != mComputedWidth) { + mComputedWidth = aAvailSpace.width - mComputedBorderPadding.left - mComputedBorderPadding.right; + mComputedWidth = PR_MAX(0, mComputedWidth); + } + if (NS_UNCONSTRAINEDSIZE != mComputedHeight) { + if (NS_UNCONSTRAINEDSIZE != aAvailSpace.height) { + mComputedHeight = aAvailSpace.height - mComputedBorderPadding.top - mComputedBorderPadding.bottom; + mComputedHeight = PR_MAX(0, mComputedHeight); + } + } + } +} + +void +nsTableRowFrame::InitChildReflowState(nsIPresContext& aPresContext, + const nsSize& aAvailSize, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsTableCellReflowState& aReflowState) +{ + nsMargin collapseBorder; + nsMargin* pCollapseBorder = nsnull; + if (aBorderCollapse) { + // we only reflow cells, so don't need to check frame type + nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame; + if (bcCellFrame) { + pCollapseBorder = bcCellFrame->GetBorderWidth(aPixelsToTwips, collapseBorder); + } + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder); + aReflowState.FixUp(aAvailSize); } void @@ -132,23 +154,6 @@ nsTableRowFrame::SetPctHeight(float aPctValue, } } -void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace) -{ - // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base - if (NS_UNCONSTRAINEDSIZE != aAvailSpace.width) { - if (NS_UNCONSTRAINEDSIZE != mComputedWidth) { - mComputedWidth = aAvailSpace.width - mComputedBorderPadding.left - mComputedBorderPadding.right; - mComputedWidth = PR_MAX(0, mComputedWidth); - } - if (NS_UNCONSTRAINEDSIZE != mComputedHeight) { - if (NS_UNCONSTRAINEDSIZE != aAvailSpace.height) { - mComputedHeight = aAvailSpace.height - mComputedBorderPadding.top - mComputedBorderPadding.bottom; - mComputedHeight = PR_MAX(0, mComputedHeight); - } - } - } -} - // 'old' is old cached cell's desired size // 'new' is new cell's size including style constraints static PRBool @@ -217,7 +222,7 @@ nsTableRowFrame::SetInitialChildList(nsIPresContext* aPresContext, kidFrame->GetNextSibling(&kidFrame)) { nsCOMPtr frameType; kidFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { if (((nsTableCellFrame*)kidFrame)->GetRowSpan() > 1) { mState |= NS_FRAME_OUTSIDE_CHILDREN; } @@ -241,7 +246,7 @@ nsTableRowFrame::AppendFrames(nsIPresContext* aPresContext, for (nsIFrame* childFrame = aFrameList; childFrame; childFrame->GetNextSibling(&childFrame)) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { // Add the cell to the cell map tableFrame->AppendCell(*aPresContext, (nsTableCellFrame&)*childFrame, GetRowIndex()); // XXX this could be optimized with some effort @@ -272,12 +277,13 @@ nsTableRowFrame::InsertFrames(nsIPresContext* aPresContext, nsTableFrame::GetTableFrame(this, tableFrame); // gather the new frames (only those which are cells) into an array - nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(aPresContext, this, aPrevFrame, nsLayoutAtoms::tableCellFrame); + nsIAtom* cellFrameType = (tableFrame->IsBorderCollapse()) ? nsLayoutAtoms::bcTableCellFrame : nsLayoutAtoms::tableCellFrame; + nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(aPresContext, this, aPrevFrame, cellFrameType); nsVoidArray cellChildren; for (nsIFrame* childFrame = aFrameList; childFrame; childFrame->GetNextSibling(&childFrame)) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { cellChildren.AppendElement(childFrame); // XXX this could be optimized with some effort tableFrame->SetNeedStrategyInit(PR_TRUE); @@ -315,7 +321,7 @@ nsTableRowFrame::RemoveFrame(nsIPresContext* aPresContext, if (tableFrame) { nsCOMPtr frameType; aOldFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)aOldFrame; PRInt32 colIndex; cellFrame->GetColIndex(colIndex); @@ -372,7 +378,7 @@ nsTableRowFrame::GetFirstCell() while (childFrame) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { return (nsTableCellFrame*)childFrame; } childFrame->GetNextSibling(&childFrame); @@ -398,7 +404,7 @@ nsTableRowFrame::DidResize(nsIPresContext* aPresContext, while (childFrame) { nsCOMPtr frameType; childFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)childFrame; nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame); @@ -534,7 +540,7 @@ nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState) for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame->GetNextSibling(&kidFrame)) { nsCOMPtr frameType; kidFrame->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nscoord availWidth = ((nsTableCellFrame *)kidFrame)->GetPriorAvailWidth(); nsSize desSize = ((nsTableCellFrame *)kidFrame)->GetDesiredSize(); if ((NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) && !mPrevInFlow) { @@ -888,28 +894,29 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, aStatus = NS_FRAME_COMPLETE; if (!mFrames.FirstChild()) return NS_OK; - nsTableFrame* tableFrame = &aTableFrame; - if (!tableFrame) return NS_ERROR_NULL_POINTER; + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + PRBool borderCollapse = (((nsTableFrame*)aTableFrame.GetFirstInFlow())->IsBorderCollapse()); + nsIFrame* tablePrevInFlow; - tableFrame->GetPrevInFlow(&tablePrevInFlow); + aTableFrame.GetPrevInFlow(&tablePrevInFlow); PRBool isPaginated; aPresContext->IsPaginated(&isPaginated); nsresult rv = NS_OK; - nscoord cellSpacingX = tableFrame->GetCellSpacingX(); + nscoord cellSpacingX = aTableFrame.GetCellSpacingX(); PRInt32 cellColSpan = 1; // must be defined here so it's set properly for non-cell kids nsTableIteration dir = (aReflowState.availableWidth == NS_UNCONSTRAINEDSIZE) ? eTableLTR : eTableDIR; nsTableIterator iter(aPresContext, *this, dir); // remember the col index of the previous cell to handle rowspans into this row - PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : tableFrame->GetColCount(); + PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount(); PRInt32 prevColIndex = firstPrevColIndex; nscoord x = 0; // running total of children x offset - nsTableFrame* tableFirstInFlow = (nsTableFrame*)tableFrame->GetFirstInFlow(); - PRBool isAutoLayout = tableFrame->IsAutoLayout(); + nsTableFrame* tableFirstInFlow = (nsTableFrame*)aTableFrame.GetFirstInFlow(); + PRBool isAutoLayout = aTableFrame.IsAutoLayout(); PRBool needToNotifyTable = PR_TRUE; nscoord paginatedHeight = 0; // If the incremental reflow command is a StyleChanged reflow and @@ -945,7 +952,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, doReflowChild = PR_FALSE; } if (aReflowState.mFlags.mSpecialHeightReflow) { - if (!isPaginated && (nsLayoutAtoms::tableCellFrame == frameType.get() && + if (!isPaginated && (IS_TABLE_CELL(frameType.get()) && !((nsTableCellFrame*)kidFrame)->NeedSpecialReflow())) { kidFrame = iter.Next(); continue; @@ -954,22 +961,22 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // Reflow the child frame if (doReflowChild) { - if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + if (IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)kidFrame; PRInt32 cellColIndex; cellFrame->GetColIndex(cellColIndex); - cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame); + cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame); x += cellSpacingX; // If the adjacent cell is in a prior row (because of a rowspan) add in the space if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) || (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) { - x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, *tableFrame, + x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, cellSpacingX, iter.IsLeftToRight()); } // Calculate the available width for the table cell using the known column widths nscoord availColWidth, availCellWidth; - CalcAvailWidth(*tableFrame, GetComputedWidth(aReflowState, *tableFrame), + CalcAvailWidth(aTableFrame, GetComputedWidth(aReflowState, aTableFrame), *cellFrame, cellSpacingX, availColWidth, availCellWidth); if (0 == availColWidth) availColWidth = NS_UNCONSTRAINEDSIZE; if (0 == availCellWidth) availCellWidth = NS_UNCONSTRAINEDSIZE; @@ -1032,6 +1039,8 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // Reflow the child nsTableCellReflowState kidReflowState(aPresContext, aReflowState, kidFrame, kidAvailSize, reason); + InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse, p2t, kidReflowState); + nsReflowStatus status; rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, x, 0, 0, status); @@ -1052,7 +1061,7 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // allow the table to determine if/how the table needs to be rebalanced if (cellToWatch && needToNotifyTable) { - needToNotifyTable = !tableFrame->CellChangedWidth(*cellFrame, oldMaxWidth, oldMaxElemWidth); + needToNotifyTable = !aTableFrame.CellChangedWidth(*cellFrame, oldMaxWidth, oldMaxElemWidth); } // If any of the cells are not complete, then we're not complete @@ -1081,10 +1090,10 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, // height may have changed, adjust descent to absorb any excess difference nscoord ascent = cellFrame->GetDesiredAscent(); nscoord descent = desiredSize.height - ascent; - UpdateHeight(desiredSize.height, ascent, descent, tableFrame, cellFrame); + UpdateHeight(desiredSize.height, ascent, descent, &aTableFrame, cellFrame); } else { - PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); + PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame); if ((1 == rowSpan) && (desiredSize.height > paginatedHeight)) { paginatedHeight = desiredSize.height; SetContentHeight(paginatedHeight); @@ -1102,13 +1111,14 @@ nsTableRowFrame::ReflowChildren(nsIPresContext* aPresContext, else {// it's an unknown frame type, give it a generic reflow and ignore the results nsTableCellReflowState kidReflowState(aPresContext, aReflowState, kidFrame, nsSize(0,0), eReflowReason_Resize); + InitChildReflowState(*aPresContext, nsSize(0,0), PR_FALSE, p2t, kidReflowState); nsHTMLReflowMetrics desiredSize(nsnull); nsReflowStatus status; ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status); kidFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED); } } - else if (nsLayoutAtoms::tableCellFrame == frameType.get()) { + else if (IS_TABLE_CELL(frameType.get())) { // we need to account for the cell's width even if it isn't reflowed nsRect rect; kidFrame->GetRect(rect); @@ -1244,7 +1254,10 @@ nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext, // update its maximum width. nsHTMLReflowMetrics cellMet(&kidMaxElementSize, isAutoLayout ? NS_REFLOW_CALC_MAX_WIDTH : 0); - nsTableCellReflowState kidRS(aPresContext, aReflowState, aNextFrame, cellAvailSize); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + nsTableCellReflowState kidRS(aPresContext, aReflowState, aNextFrame, cellAvailSize, + aReflowState.reason); + InitChildReflowState(*aPresContext, cellAvailSize, aTableFrame.IsBorderCollapse(), p2t, kidRS); // Remember the current desired size, we'll need it later nscoord oldCellMinWidth = cellFrame->GetPass1MaxElementWidth(); @@ -1492,13 +1505,20 @@ nsTableRowFrame::ReflowCellFrame(nsIPresContext* aPresContext, nscoord aAvailableHeight, nsReflowStatus& aStatus) { + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(0); + // Reflow the cell frame with the specified height. Use the existing width nsSize cellSize; aCellFrame->GetSize(cellSize); nsSize availSize(cellSize.width, aAvailableHeight); + PRBool borderCollapse = ((nsTableFrame*)tableFrame->GetFirstInFlow())->IsBorderCollapse(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); nsTableCellReflowState cellReflowState(aPresContext, aReflowState, aCellFrame, availSize, eReflowReason_Resize); + InitChildReflowState(*aPresContext, availSize, borderCollapse, p2t, cellReflowState); + nsHTMLReflowMetrics desiredSize(nsnull); ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState, @@ -1534,7 +1554,7 @@ nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame, for (nsIFrame* child = mFrames.FirstChild(); child; child->GetNextSibling(&child)) { nsCOMPtr frameType; child->GetFrameType(getter_AddRefs(frameType)); - if (nsLayoutAtoms::tableCellFrame != frameType.get()) { + if (!IS_TABLE_CELL(frameType.get())) { nsTableCellFrame* cellFrame = (nsTableCellFrame*)child; PRInt32 colIndex; cellFrame->GetColIndex(colIndex); diff --git a/mozilla/layout/tables/nsTableRowFrame.h b/mozilla/layout/tables/nsTableRowFrame.h index 552ee7910b3..d05dc5eab6a 100644 --- a/mozilla/layout/tables/nsTableRowFrame.h +++ b/mozilla/layout/tables/nsTableRowFrame.h @@ -42,6 +42,7 @@ class nsTableFrame; class nsTableCellFrame; +struct nsTableCellReflowState; #ifdef DEBUG_TABLE_REFLOW_TIMING class nsReflowTimer; @@ -240,6 +241,12 @@ public: nscoord GetUnpaginatedHeight(nsIPresContext* aPresContext); void SetUnpaginatedHeight(nsIPresContext* aPresContext, nscoord aValue); + nscoord GetTopBCBorderWidth(float* aPixelsToTwips = nsnull); + void SetTopBCBorderWidth(nscoord aWidth); + nscoord GetBottomBCBorderWidth(float* aPixelsToTwips = nsnull); + void SetBottomBCBorderWidth(nscoord aWidth); + nsMargin* GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder); protected: /** protected constructor. @@ -247,6 +254,12 @@ protected: */ nsTableRowFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + const nsSize& aAvailSize, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsTableCellReflowState& aReflowState); + /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const; @@ -315,6 +328,10 @@ private: nscoord mMaxCellAscent; // does include cells with rowspan > 1 nscoord mMaxCellDescent; // does *not* include cells with rowspan > 1 + // border widths in pixels in the collapsing border model + unsigned mTopBorderWidth:8; + unsigned mBottomBorderWidth:8; + #ifdef DEBUG_TABLE_REFLOW_TIMING public: nsReflowTimer* mTimer; @@ -421,4 +438,37 @@ inline void nsTableRowFrame::SetHasUnpaginatedHeight(PRBool aValue) } } +inline nscoord nsTableRowFrame::GetTopBCBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mTopBorderWidth) : mTopBorderWidth; + return width; +} + +inline void nsTableRowFrame::SetTopBCBorderWidth(nscoord aWidth) +{ + mTopBorderWidth = aWidth; +} + +inline nscoord nsTableRowFrame::GetBottomBCBorderWidth(float* aPixelsToTwips) +{ + nscoord width = (aPixelsToTwips) ? NSToCoordRound(*aPixelsToTwips * mBottomBorderWidth) : mBottomBorderWidth; + return width; +} + +inline void nsTableRowFrame::SetBottomBCBorderWidth(nscoord aWidth) +{ + mBottomBorderWidth = aWidth; +} + +inline nsMargin* nsTableRowFrame::GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) +{ + aBorder.left = aBorder.right = 0; + + aBorder.top = NSToCoordRound(aPixelsToTwips * mTopBorderWidth); + aBorder.bottom = NSToCoordRound(aPixelsToTwips * mBottomBorderWidth); + + return &aBorder; +} + #endif diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.cpp b/mozilla/layout/tables/nsTableRowGroupFrame.cpp index f0ff97530bb..2405a50a342 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/tables/nsTableRowGroupFrame.cpp @@ -180,7 +180,7 @@ nsTableRowGroupFrame::InitRepeatedFrame(nsIPresContext* aPresContext, nsIAtom* frameType; copyCellFrame->GetFrameType(&frameType); - if (nsLayoutAtoms::tableCellFrame == frameType) { + if (IS_TABLE_CELL(frameType)) { #ifdef NS_DEBUG nsIContent* content1; nsIContent* content2; @@ -360,6 +360,28 @@ nsTableRowGroupFrame::PlaceChild(nsIPresContext* aPresContext, } } +void +nsTableRowGroupFrame::InitChildReflowState(nsIPresContext& aPresContext, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsHTMLReflowState& aReflowState) +{ + nsMargin collapseBorder; + nsMargin padding(0,0,0,0); + nsMargin* pCollapseBorder = nsnull; + if (aBorderCollapse) { + if (aReflowState.frame) { + nsCOMPtr fType; + aReflowState.frame->GetFrameType(getter_AddRefs(fType)); + if (nsLayoutAtoms::tableRowFrame == fType.get()) { + nsTableRowFrame* rowFrame = (nsTableRowFrame*)aReflowState.frame; + pCollapseBorder = rowFrame->GetBCBorderWidth(aPixelsToTwips, collapseBorder); + } + } + } + aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, &padding); +} + // Reflow the frames we've already created. If aDirtyOnly is set then only // reflow dirty frames. This assumes that all of the dirty frames are contiguous. NS_METHOD @@ -372,8 +394,10 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, nsTableRowFrame** aFirstRowReflowed) { nsTableFrame* tableFrame = nsnull; - nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); - if (NS_FAILED(rv) || !tableFrame) return rv; + nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(rv); + + PRBool borderCollapse = tableFrame->IsBorderCollapse(); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); nscoord cellSpacingY = tableFrame->GetCellSpacingY(); @@ -439,6 +463,7 @@ nsTableRowGroupFrame::ReflowChildren(nsIPresContext* aPresContext, } nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, kidFrame, kidAvailSize, reason); + InitChildReflowState(*aPresContext, borderCollapse, p2t, kidReflowState); // If this isn't the first row frame, then we can't be at the top of // the page anymore... @@ -555,7 +580,7 @@ HasMoreThanOneCell(nsTableCellMap* aCellMap, PRInt32 colIndex = 0; PRInt32 count = 0; do { - cellData = aCellMap->GetCellAt(aRowIndex, colIndex); + cellData = aCellMap->GetDataAt(aRowIndex, colIndex); if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan())) count++; if (count > 1) @@ -998,8 +1023,8 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, presShell->GetStyleSet(getter_AddRefs(styleSet)); if (!styleSet) {NS_ASSERTION(PR_FALSE, "no style set"); return NS_ERROR_NULL_POINTER;} - float p2t; - aPresContext->GetPixelsToTwips(&p2t); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + PRBool borderCollapse = ((nsTableFrame*)aTableFrame->GetFirstInFlow())->IsBorderCollapse(); nscoord availWidth = nsTableFrame::RoundToPixel(aReflowState.availableWidth, p2t); nscoord availHeight = nsTableFrame::RoundToPixel(aReflowState.availableHeight, p2t); @@ -1022,9 +1047,11 @@ nsTableRowGroupFrame::SplitRowGroup(nsIPresContext* aPresContext, // row or there is at least 5% of the current page available if (!prevRowFrame || (availHeight - aDesiredSize.height > pageHeight / 20)) { // Reflow the row in the available space and have it split - nsSize availSize(availWidth, availHeight - bounds.y); - nsHTMLReflowState rowReflowState(aPresContext, aReflowState, rowFrame, - availSize, eReflowReason_Resize); + nsSize availSize(availWidth, availHeight - bounds.y); + nsHTMLReflowState rowReflowState(aPresContext, aReflowState, rowFrame, availSize, + eReflowReason_Resize); + InitChildReflowState(*aPresContext, borderCollapse, p2t, rowReflowState); + nsHTMLReflowMetrics desiredSize(nsnull); rv = ReflowChild(rowFrame, aPresContext, desiredSize, rowReflowState, @@ -1530,6 +1557,10 @@ nsTableRowGroupFrame::IR_TargetIsChild(nsIPresContext* aPresContext, { nsresult rv; + nsTableFrame* tableFrame = nsnull; + nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(NS_ERROR_NULL_POINTER); + GET_PIXELS_TO_TWIPS(aPresContext, p2t); + // Recover the state as if aNextFrame is about to be reflowed RecoverState(aReflowState, aNextFrame); @@ -1540,9 +1571,11 @@ nsTableRowGroupFrame::IR_TargetIsChild(nsIPresContext* aPresContext, // Reflow the child giving it as much room as it wants. We'll deal with // splitting later after final determination of rows heights taking into // account cells with row spans... - nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE); - nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, - aNextFrame, kidAvailSize); + nsSize kidAvailSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState.reflowState, aNextFrame, + kidAvailSize, aReflowState.reason); + InitChildReflowState(*aPresContext, tableFrame->IsBorderCollapse(), p2t, kidReflowState); + nsSize kidMaxElementSize; nsHTMLReflowMetrics desiredSize(aDesiredSize.maxElementSize ? &kidMaxElementSize : nsnull, aDesiredSize.mFlags); @@ -1715,6 +1748,28 @@ nsTableRowGroupFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) cons } #endif +nsMargin* +nsTableRowGroupFrame::GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder) +{ + aBorder.left = aBorder.right = 0; + + nsTableRowFrame* firstRowFrame = nsnull; + nsTableRowFrame* lastRowFrame = nsnull; + for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) { + if(!firstRowFrame) { + firstRowFrame = rowFrame; + } + lastRowFrame = rowFrame; + } + if (firstRowFrame) { + aBorder.top = firstRowFrame->GetTopBCBorderWidth(&aPixelsToTwips); + aBorder.bottom = lastRowFrame->GetBottomBCBorderWidth(&aPixelsToTwips); + } + + return &aBorder; +} + //nsILineIterator methods for nsTableFrame NS_IMETHODIMP nsTableRowGroupFrame::GetNumLines(PRInt32* aResult) @@ -1757,7 +1812,7 @@ nsTableRowGroupFrame::GetLine(PRInt32 aLineNumber, *aLineFlags = 0;/// should we fill these in later? // not gonna touch aLineBounds right now - CellData* firstCellData = cellMap->GetCellAt(aLineNumber, 0); + CellData* firstCellData = cellMap->GetDataAt(aLineNumber, 0); if(!firstCellData) return NS_ERROR_FAILURE; @@ -1767,7 +1822,7 @@ nsTableRowGroupFrame::GetLine(PRInt32 aLineNumber, while((aLineNumber > 0)&&(!(*aFirstFrameOnLine))) { aLineNumber--; - firstCellData = cellMap->GetCellAt(aLineNumber, 0); + firstCellData = cellMap->GetDataAt(aLineNumber, 0); *aFirstFrameOnLine = (nsIFrame*)firstCellData->GetCellFrame(); } } @@ -1847,7 +1902,7 @@ nsTableRowGroupFrame::FindFrameAt(PRInt32 aLineNumber, PRBool gotParentRect = PR_FALSE; for(int i =0;i < cellCount; i++) { - cellData = cellMap->GetCellAt(aLineNumber, i); + cellData = cellMap->GetDataAt(aLineNumber, i); tempFrame = (nsIFrame*)cellData->GetCellFrame(); if(!tempFrame) @@ -1919,11 +1974,11 @@ nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32& colIndexRef = colIndex; cellFrame->GetColIndex(colIndexRef); - CellData* cellData = cellMap->GetCellAt(aLineNumber, colIndex + 1); + CellData* cellData = cellMap->GetDataAt(aLineNumber, colIndex + 1); if(!cellData)// if this isnt a valid cell, drop down and check the next line { - cellData = cellMap->GetCellAt(aLineNumber + 1, 0); + cellData = cellMap->GetDataAt(aLineNumber + 1, 0); if(!cellData) { //*aFrame = nsnull; @@ -1940,14 +1995,14 @@ nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame, while((tempCol > 0) && (!aFrame)) { tempCol--; - cellData = cellMap->GetCellAt(aLineNumber, tempCol); + cellData = cellMap->GetDataAt(aLineNumber, tempCol); aFrame = (nsIFrame*)cellData->GetCellFrame(); if(!aFrame && (tempCol==0)) { while((tempRow > 0) && (!aFrame)) { tempRow--; - cellData = cellMap->GetCellAt(tempRow, 0); + cellData = cellMap->GetDataAt(tempRow, 0); aFrame = (nsIFrame*)cellData->GetCellFrame(); } } diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.h b/mozilla/layout/tables/nsTableRowGroupFrame.h index 0a3859755b4..d259a63bdfb 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.h +++ b/mozilla/layout/tables/nsTableRowGroupFrame.h @@ -216,6 +216,8 @@ public: nscoord GetHeightOfRows(nsIPresContext* aPresContext); nscoord GetHeightBasis(const nsHTMLReflowState& aReflowState); + nsMargin* GetBCBorderWidth(float aPixelsToTwips, + nsMargin& aBorder); // nsILineIterator methods public: NS_IMETHOD GetNumLines(PRInt32* aResult); @@ -251,6 +253,11 @@ public: protected: nsTableRowGroupFrame(); + void InitChildReflowState(nsIPresContext& aPresContext, + PRBool aBorderCollapse, + float aPixelsToTwips, + nsHTMLReflowState& aReflowState); + /** implement abstract method on nsHTMLContainerFrame */ virtual PRIntn GetSkipSides() const;