#413: update to HTML colspan/rowspan from M1359822, plus M1271126 + M1373095 (no tests)

This commit is contained in:
Cameron Kaiser 2017-07-10 03:14:31 -07:00
parent d1001e7efa
commit 6958789e8f
13 changed files with 107 additions and 97 deletions

View File

@ -1491,6 +1491,40 @@ nsAttrValue::ParseIntWithBounds(const nsAString& aString,
return true; return true;
} }
void
nsAttrValue::ParseClampedNonNegativeInt(const nsAString& aString,
int32_t aDefault, int32_t aMin,
int32_t aMax)
{
ResetIfSet();
nsContentUtils::ParseHTMLIntegerResultFlags result;
int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) ||
(result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
if (result & nsContentUtils::eParseHTMLInteger_ErrorOverflow) {
if (result & nsContentUtils::eParseHTMLInteger_Negative) {
val = aDefault;
} else {
val = aMax;
}
nonStrict = true;
} else if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 0) {
val = aDefault;
nonStrict = true;
} else if (val < aMin) {
val = aMin;
nonStrict = true;
} else if (val > aMax) {
val = aMax;
nonStrict = true;
}
SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
}
bool bool
nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString) nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString)
{ {

View File

@ -326,6 +326,19 @@ public:
*/ */
bool ParseNonNegativeIntValue(const nsAString& aString); bool ParseNonNegativeIntValue(const nsAString& aString);
/**
* Parse a string value into a clamped non-negative integer.
* This method follows the rules for parsing non-negative integer from:
* https://html.spec.whatwg.org/multipage/infrastructure.html#clamped-to-the-range
*
* @param aString the string to parse
* @param aDefault value to return for negative or invalid values
* @param aMin minimum value
* @param aMax maximum value
*/
void ParseClampedNonNegativeInt(const nsAString& aString, int32_t aDefault,
int32_t aMin, int32_t aMax);
/** /**
* Parse a string value into a positive integer. * Parse a string value into a positive integer.
* This method follows the rules for parsing non-negative integer from: * This method follows the rules for parsing non-negative integer from:

View File

@ -1057,9 +1057,10 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
return 0; return 0;
} }
bool negate = false; int sign = 1;
if (*iter == char16_t('-')) { if (*iter == char16_t('-')) {
negate = true; sign = -1;
result |= eParseHTMLInteger_Negative;
++iter; ++iter;
} else if (*iter == char16_t('+')) { } else if (*iter == char16_t('+')) {
result |= eParseHTMLInteger_NonStandard; result |= eParseHTMLInteger_NonStandard;
@ -1083,7 +1084,7 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
while (iter != end) { while (iter != end) {
if (*iter >= char16_t('0') && *iter <= char16_t('9')) { if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
value = (value * 10) + (*iter - char16_t('0')); value = (value * 10) + (*iter - char16_t('0')) * sign;
++iter; ++iter;
if (!value.isValid()) { if (!value.isValid()) {
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow; result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
@ -1104,16 +1105,9 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue; result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
} }
if (value.isValid() && negate) {
value = -value;
// Checking the special case of -0.
if (value == 0) {
result |= eParseHTMLInteger_NonStandard;
}
}
if (value.isValid() && if (value.isValid() &&
(leadingZeros > 1 || (leadingZeros == 1 && !(value == 0)))) { ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
(sign == -1 && value == 0))) {
result |= eParseHTMLInteger_NonStandard; result |= eParseHTMLInteger_NonStandard;
} }

View File

@ -439,7 +439,9 @@ public:
// Set if one or more error flags were set. // Set if one or more error flags were set.
eParseHTMLInteger_Error = 1 << 3, eParseHTMLInteger_Error = 1 << 3,
eParseHTMLInteger_ErrorNoValue = 1 << 4, eParseHTMLInteger_ErrorNoValue = 1 << 4,
eParseHTMLInteger_ErrorOverflow = 1 << 5 eParseHTMLInteger_ErrorOverflow = 1 << 5,
// Use this flag to detect the difference between overflow and underflow
eParseHTMLInteger_Negative = 1 << 6,
}; };
static int32_t ParseHTMLInteger(const nsAString& aValue, static int32_t ParseHTMLInteger(const nsAString& aValue,
ParseHTMLIntegerResultFlags *aResult); ParseHTMLIntegerResultFlags *aResult);

View File

@ -384,28 +384,16 @@ HTMLTableCellElement::ParseAttribute(int32_t aNamespaceID,
return aResult.ParseIntWithBounds(aValue, 0); return aResult.ParseIntWithBounds(aValue, 0);
} }
if (aAttribute == nsGkAtoms::colspan) { if (aAttribute == nsGkAtoms::colspan) {
bool res = aResult.ParseIntWithBounds(aValue, -1); aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN);
if (res) { return true;
int32_t val = aResult.GetIntegerValue();
// reset large colspan values as IE and opera do
// quirks mode does not honor the special html 4 value of 0
if (val > MAX_COLSPAN || val < 0 ||
(0 == val && InNavQuirksMode(OwnerDoc()))) {
aResult.SetTo(1, &aValue);
}
}
return res;
} }
if (aAttribute == nsGkAtoms::rowspan) { if (aAttribute == nsGkAtoms::rowspan) {
bool res = aResult.ParseIntWithBounds(aValue, -1, MAX_ROWSPAN); aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN);
if (res) { // quirks mode does not honor the special html 4 value of 0
int32_t val = aResult.GetIntegerValue(); if (aResult.GetIntegerValue() == 0 && InNavQuirksMode(OwnerDoc())) {
// quirks mode does not honor the special html 4 value of 0 aResult.SetTo(1, &aValue);
if (val < 0 || (0 == val && InNavQuirksMode(OwnerDoc()))) {
aResult.SetTo(1, &aValue);
}
} }
return res; return true;
} }
if (aAttribute == nsGkAtoms::height) { if (aAttribute == nsGkAtoms::height) {
return aResult.ParseSpecialIntValue(aValue); return aResult.ParseSpecialIntValue(aValue);

View File

@ -37,7 +37,7 @@ public:
} }
void SetColSpan(uint32_t aColSpan, ErrorResult& aError) void SetColSpan(uint32_t aColSpan, ErrorResult& aError)
{ {
SetHTMLIntAttr(nsGkAtoms::colspan, aColSpan, aError); SetUnsignedIntAttr(nsGkAtoms::colspan, aColSpan, aError);
} }
uint32_t RowSpan() const uint32_t RowSpan() const
{ {
@ -45,7 +45,7 @@ public:
} }
void SetRowSpan(uint32_t aRowSpan, ErrorResult& aError) void SetRowSpan(uint32_t aRowSpan, ErrorResult& aError)
{ {
SetHTMLIntAttr(nsGkAtoms::rowspan, aRowSpan, aError); SetUnsignedIntAttr(nsGkAtoms::rowspan, aRowSpan, aError);
} }
//already_AddRefed<nsDOMSettableTokenList> Headers() const; //already_AddRefed<nsDOMSettableTokenList> Headers() const;
void GetHeaders(DOMString& aHeaders) void GetHeaders(DOMString& aHeaders)

View File

@ -9,6 +9,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
#include "nsCRT.h" #include "nsCRT.h"
#include "nsLayoutStylesheetCache.h" #include "nsLayoutStylesheetCache.h"
#include "nsRuleData.h" #include "nsRuleData.h"
@ -150,8 +151,10 @@ nsMathMLElement::ParseAttribute(int32_t aNamespaceID,
const nsAString& aValue, const nsAString& aValue,
nsAttrValue& aResult) nsAttrValue& aResult)
{ {
MOZ_ASSERT(IsMathMLElement());
if (aNamespaceID == kNameSpaceID_None) { if (aNamespaceID == kNameSpaceID_None) {
if (IsMathMLElement(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) { if (mNodeInfo->Equals(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) {
WarnDeprecated(nsGkAtoms::mode->GetUTF16String(), WarnDeprecated(nsGkAtoms::mode->GetUTF16String(),
nsGkAtoms::display->GetUTF16String(), OwnerDoc()); nsGkAtoms::display->GetUTF16String(), OwnerDoc());
} }
@ -165,6 +168,16 @@ nsMathMLElement::ParseAttribute(int32_t aNamespaceID,
aAttribute == nsGkAtoms::mathbackground_) { aAttribute == nsGkAtoms::mathbackground_) {
return aResult.ParseColor(aValue); return aResult.ParseColor(aValue);
} }
if (mNodeInfo->Equals(nsGkAtoms::mtd_)) {
if (aAttribute == nsGkAtoms::columnspan_) {
aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN);
return true;
}
if (aAttribute == nsGkAtoms::rowspan) {
aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN);
return true;
}
}
} }
return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute, return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute,
@ -209,6 +222,8 @@ static Element::MappedAttributeEntry sDirStyles[] = {
bool bool
nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
{ {
MOZ_ASSERT(IsMathMLElement());
static const MappedAttributeEntry* const mtableMap[] = { static const MappedAttributeEntry* const mtableMap[] = {
sMtableStyles, sMtableStyles,
sCommonPresStyles sCommonPresStyles
@ -240,10 +255,10 @@ nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math)) if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math))
return FindAttributeDependence(aAttribute, mstyleMap); return FindAttributeDependence(aAttribute, mstyleMap);
if (IsMathMLElement(nsGkAtoms::mtable_)) if (mNodeInfo->Equals(nsGkAtoms::mtable_))
return FindAttributeDependence(aAttribute, mtableMap); return FindAttributeDependence(aAttribute, mtableMap);
if (IsMathMLElement(nsGkAtoms::mrow_)) if (mNodeInfo->Equals(nsGkAtoms::mrow_))
return FindAttributeDependence(aAttribute, mrowMap); return FindAttributeDependence(aAttribute, mrowMap);
if (IsAnyOfMathMLElements(nsGkAtoms::maction_, if (IsAnyOfMathMLElements(nsGkAtoms::maction_,

View File

@ -1159,47 +1159,6 @@ nsMathMLmtdFrame::Init(nsIContent* aContent,
RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
} }
int32_t
nsMathMLmtdFrame::GetRowSpan()
{
int32_t rowspan = 1;
// Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
if (mContent->IsMathMLElement(nsGkAtoms::mtd_) &&
!StyleContext()->GetPseudo()) {
nsAutoString value;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
if (!value.IsEmpty()) {
nsresult error;
rowspan = value.ToInteger(&error);
if (NS_FAILED(error) || rowspan < 0)
rowspan = 1;
rowspan = std::min(rowspan, MAX_ROWSPAN);
}
}
return rowspan;
}
int32_t
nsMathMLmtdFrame::GetColSpan()
{
int32_t colspan = 1;
// Don't look at the content's colspan if we're not an mtd or a pseudo cell.
if (mContent->IsMathMLElement(nsGkAtoms::mtd_) &&
!StyleContext()->GetPseudo()) {
nsAutoString value;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
if (!value.IsEmpty()) {
nsresult error;
colspan = value.ToInteger(&error);
if (NS_FAILED(error) || colspan < 0 || colspan > MAX_COLSPAN)
colspan = 1;
}
}
return colspan;
}
nsresult nsresult
nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID, nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute, nsIAtom* aAttribute,

View File

@ -256,8 +256,6 @@ public:
nsDisplayListBuilder* aBuilder, nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) override; const nsDisplayListSet& aLists) override;
virtual int32_t GetRowSpan() override;
virtual int32_t GetColSpan() override;
virtual bool IsFrameOfType(uint32_t aFlags) const override virtual bool IsFrameOfType(uint32_t aFlags) const override
{ {
return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML)); return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));

View File

@ -6,6 +6,7 @@
#define CellData_h__ #define CellData_h__
#include "nsISupports.h" #include "nsISupports.h"
#include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
#include "nsCoord.h" #include "nsCoord.h"
#include "mozilla/gfx/Types.h" #include "mozilla/gfx/Types.h"
#include "mozilla/WritingModes.h" #include "mozilla/WritingModes.h"
@ -16,10 +17,6 @@ class nsCellMap;
class BCCellData; class BCCellData;
#define MAX_ROWSPAN 65534 // the cellmap can not handle more.
#define MAX_COLSPAN 1000 // limit as IE and opera do. If this ever changes,
// change COL_SPAN_OFFSET/COL_SPAN_SHIFT accordingly.
/** /**
* Data stored by nsCellMap to rationalize rowspan and colspan cells. * Data stored by nsCellMap to rationalize rowspan and colspan cells.
*/ */

View File

@ -7,6 +7,10 @@
#include "nsQueryFrame.h" #include "nsQueryFrame.h"
#define MAX_ROWSPAN 65534 // the cellmap can not handle more.
#define MAX_COLSPAN 1000 // limit as IE and opera do. If this ever changes,
// change COL_SPAN_OFFSET/COL_SPAN_SHIFT accordingly.
/** /**
* nsITableCellLayout * nsITableCellLayout
* interface for layout objects that act like table cells. * interface for layout objects that act like table cells.

View File

@ -735,16 +735,18 @@ nsTableCellFrame::GetCellBaseline() const
borderPadding; borderPadding;
} }
int32_t nsTableCellFrame::GetRowSpan() int32_t
nsTableCellFrame::GetRowSpan()
{ {
int32_t rowSpan=1; int32_t rowSpan=1;
nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
// Don't look at the content's rowspan if we're a pseudo cell // Don't look at the content's rowspan if we're a pseudo cell
if (hc && !StyleContext()->GetPseudo()) { if (!StyleContext()->GetPseudo()) {
const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan); dom::Element* elem = mContent->AsElement();
const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan);
// Note that we don't need to check the tag name, because only table cells // Note that we don't need to check the tag name, because only table cells
// and table headers parse the "rowspan" attribute into an integer. // (including MathML <mtd>) and table headers parse the "rowspan" attribute
// into an integer.
if (attr && attr->Type() == nsAttrValue::eInteger) { if (attr && attr->Type() == nsAttrValue::eInteger) {
rowSpan = attr->GetIntegerValue(); rowSpan = attr->GetIntegerValue();
} }
@ -752,16 +754,20 @@ int32_t nsTableCellFrame::GetRowSpan()
return rowSpan; return rowSpan;
} }
int32_t nsTableCellFrame::GetColSpan() int32_t
nsTableCellFrame::GetColSpan()
{ {
int32_t colSpan=1; int32_t colSpan=1;
nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
// Don't look at the content's colspan if we're a pseudo cell // Don't look at the content's colspan if we're a pseudo cell
if (hc && !StyleContext()->GetPseudo()) { if (!StyleContext()->GetPseudo()) {
const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan); dom::Element* elem = mContent->AsElement();
const nsAttrValue* attr = elem->GetParsedAttr(
MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan_
: nsGkAtoms::colspan);
// Note that we don't need to check the tag name, because only table cells // Note that we don't need to check the tag name, because only table cells
// and table headers parse the "colspan" attribute into an integer. // (including MathML <mtd>) and table headers parse the "colspan" attribute
// into an integer.
if (attr && attr->Type() == nsAttrValue::eInteger) { if (attr && attr->Type() == nsAttrValue::eInteger) {
colSpan = attr->GetIntegerValue(); colSpan = attr->GetIntegerValue();
} }

View File

@ -168,11 +168,11 @@ public:
/** /**
* return the cell's specified row span. this is what was specified in the * return the cell's specified row span. this is what was specified in the
* content model or in the style info, and is always >= 1. * content model or in the style info, and is always >= 0.
* to get the effective row span (the actual value that applies), use GetEffectiveRowSpan() * to get the effective row span (the actual value that applies), use GetEffectiveRowSpan()
* @see nsTableFrame::GetEffectiveRowSpan() * @see nsTableFrame::GetEffectiveRowSpan()
*/ */
virtual int32_t GetRowSpan(); int32_t GetRowSpan();
// there is no set row index because row index depends on the cell's parent row only // there is no set row index because row index depends on the cell's parent row only
@ -194,7 +194,7 @@ public:
* to get the effective col span (the actual value that applies), use GetEffectiveColSpan() * to get the effective col span (the actual value that applies), use GetEffectiveColSpan()
* @see nsTableFrame::GetEffectiveColSpan() * @see nsTableFrame::GetEffectiveColSpan()
*/ */
virtual int32_t GetColSpan(); int32_t GetColSpan();
/** return the cell's column index (starting at 0 for the first column) */ /** return the cell's column index (starting at 0 for the first column) */
virtual nsresult GetColIndex(int32_t &aColIndex) const override; virtual nsresult GetColIndex(int32_t &aColIndex) const override;