Bug 605466 - Implement new spec-based limits for formatting element proliferation in the HTML5 parsing algorithm. rs=jonas, a=blocking2.0-betaN.

--HG--
extra : rebase_source : b8d9c7fb5c4ef094af89ed9c29d49bd38d8fcca1
This commit is contained in:
Henri Sivonen 2010-10-15 12:23:42 +03:00
Родитель 01fa16e8c5
Коммит e00c8d1067
12 изменённых файлов: 186 добавлений и 38 удалений

Просмотреть файл

@ -464,6 +464,33 @@ public final class HtmlAttributes implements Attributes {
return clone; // XXX!!!
}
public boolean equalsAnother(HtmlAttributes other) {
assert mode == 0 || mode == 3 : "Trying to compare attributes in foreign content.";
int otherLength = other.getLength();
if (length != otherLength) {
return false;
}
for (int i = 0; i < length; i++) {
// Work around the limitations of C++
boolean found = false;
// The comparing just the local names is OK, since these attribute
// holders are both supposed to belong to HTML formatting elements
@Local String ownLocal = names[i].getLocal(AttributeName.HTML);
for (int j = 0; j < otherLength; j++) {
if (ownLocal == other.names[j].getLocal(AttributeName.HTML)) {
found = true;
if (!Portability.stringEqualsString(values[i], other.values[j])) {
return false;
}
}
}
if (!found) {
return false;
}
}
return true;
}
// [NOCPP[
void processNonNcNames(TreeBuilder<?> treeBuilder, XmlViolationPolicy namePolicy) throws SAXException {

Просмотреть файл

@ -159,6 +159,10 @@ public final class Portability {
return literal.equals(string);
}
public static boolean stringEqualsString(String one, String other) {
return one.equals(other);
}
public static void delete(Object o) {
}

Просмотреть файл

@ -337,8 +337,6 @@ public abstract class TreeBuilder<T> implements TokenHandler,
private static final int NOT_FOUND_ON_STACK = Integer.MAX_VALUE;
private static final int AAA_MAX_ITERATIONS = 10;
// [NOCPP[
private static final @Local String HTML_LOCAL = "html";
@ -1946,6 +1944,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
case FONT:
reconstructTheActiveFormattingElements();
maybeForgetEarlierDuplicateFormattingElement(elementName.name, attributes);
appendToCurrentNodeAndPushFormattingElementMayFoster(
"http://www.w3.org/1999/xhtml",
elementName, attributes);
@ -3529,12 +3528,6 @@ public abstract class TreeBuilder<T> implements TokenHandler,
}
}
break endtagloop;
case A:
case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
case FONT:
case NOBR:
adoptionAgencyEndTag(name);
break endtagloop;
case OBJECT:
case MARQUEE_OR_APPLET:
eltPos = findLastInScope(name);
@ -3593,6 +3586,14 @@ public abstract class TreeBuilder<T> implements TokenHandler,
} else {
// fall through
}
case A:
case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
case FONT:
case NOBR:
if (adoptionAgencyEndTag(name)) {
break endtagloop;
}
// else handle like any other tag
default:
if (isCurrent(name)) {
pop();
@ -4326,10 +4327,10 @@ public abstract class TreeBuilder<T> implements TokenHandler,
listPtr--;
}
private void adoptionAgencyEndTag(@Local String name) throws SAXException {
private boolean adoptionAgencyEndTag(@Local String name) throws SAXException {
// If you crash around here, perhaps some stack node variable claimed to
// be a weak ref isn't.
for (int i = 0; i < AAA_MAX_ITERATIONS; ++i) {
for (int i = 0; i < 8; ++i) {
int formattingEltListPos = listPtr;
while (formattingEltListPos > -1) {
StackNode<T> listNode = listOfActiveFormattingElements[formattingEltListPos]; // weak
@ -4343,8 +4344,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
formattingEltListPos--;
}
if (formattingEltListPos == -1) {
err("No element \u201C" + name + "\u201D to close.");
return;
return false;
}
StackNode<T> formattingElt = listOfActiveFormattingElements[formattingEltListPos]; // this
// *looks*
@ -4372,11 +4372,11 @@ public abstract class TreeBuilder<T> implements TokenHandler,
if (formattingEltStackPos == -1) {
err("No element \u201C" + name + "\u201D to close.");
removeFromListOfActiveFormattingElements(formattingEltListPos);
return;
return true;
}
if (!inScope) {
err("No element \u201C" + name + "\u201D to close.");
return;
return true;
}
// stackPos now points to the formatting element and it is in scope
if (errorHandler != null && formattingEltStackPos != currentPtr) {
@ -4396,7 +4396,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
pop();
}
removeFromListOfActiveFormattingElements(formattingEltListPos);
return;
return true;
}
StackNode<T> commonAncestor = stack[formattingEltStackPos - 1]; // weak
// ref
@ -4405,7 +4405,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
int bookmark = formattingEltListPos;
int nodePos = furthestBlockPos;
StackNode<T> lastNode = furthestBlock; // weak ref
for (int j = 0; j < AAA_MAX_ITERATIONS; ++j) {
for (int j = 0; j < 3; ++j) {
nodePos--;
StackNode<T> node = stack[nodePos]; // weak ref
int nodeListPos = findInListOfActiveFormattingElements(node);
@ -4483,6 +4483,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
insertIntoStack(formattingClone, furthestBlockPos);
Portability.releaseElement(clone);
}
return true;
}
private void insertIntoStack(StackNode<T> node, int position)
@ -4534,6 +4535,26 @@ public abstract class TreeBuilder<T> implements TokenHandler,
return -1;
}
private void maybeForgetEarlierDuplicateFormattingElement(
@Local String name, HtmlAttributes attributes) throws SAXException {
int candidate = -1;
int count = 0;
for (int i = listPtr; i >= 0; i--) {
StackNode<T> node = listOfActiveFormattingElements[i];
if (node == null) {
break;
}
if (node.name == name && node.attributes.equalsAnother(attributes)) {
candidate = i;
++count;
}
}
if (count >= 3) {
removeFromListOfActiveFormattingElements(candidate);
}
}
private int findLastOrRoot(@Local String name) {
for (int i = currentPtr; i > 0; i--) {
if (stack[i].name == name) {

Просмотреть файл

@ -235,6 +235,32 @@ nsHtml5HtmlAttributes::cloneAttributes(nsHtml5AtomTable* interner)
return clone;
}
PRBool
nsHtml5HtmlAttributes::equalsAnother(nsHtml5HtmlAttributes* other)
{
PRInt32 otherLength = other->getLength();
if (length != otherLength) {
return PR_FALSE;
}
for (PRInt32 i = 0; i < length; i++) {
PRBool found = PR_FALSE;
nsIAtom* ownLocal = names[i]->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML);
for (PRInt32 j = 0; j < otherLength; j++) {
if (ownLocal == other->names[j]->getLocal(NS_HTML5ATTRIBUTE_NAME_HTML)) {
found = PR_TRUE;
if (!nsHtml5Portability::stringEqualsString(values[i], other->values[j])) {
return PR_FALSE;
}
}
}
if (!found) {
return PR_FALSE;
}
}
return PR_TRUE;
}
void
nsHtml5HtmlAttributes::initializeStatics()
{

Просмотреть файл

@ -88,6 +88,7 @@ class nsHtml5HtmlAttributes
void adjustForMath();
void adjustForSvg();
nsHtml5HtmlAttributes* cloneAttributes(nsHtml5AtomTable* interner);
PRBool equalsAnother(nsHtml5HtmlAttributes* other);
static void initializeStatics();
static void releaseStatics();
};

Просмотреть файл

@ -179,6 +179,12 @@ nsHtml5Portability::literalEqualsString(const char* literal, nsString* string)
return string->EqualsASCII(literal);
}
PRBool
nsHtml5Portability::stringEqualsString(nsString* one, nsString* other)
{
return one->Equals(*other);
}
void
nsHtml5Portability::initializeStatics()
{

Просмотреть файл

@ -77,6 +77,7 @@ class nsHtml5Portability
static PRBool lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(const char* lowerCaseLiteral, nsString* string);
static PRBool lowerCaseLiteralEqualsIgnoreAsciiCaseString(const char* lowerCaseLiteral, nsString* string);
static PRBool literalEqualsString(const char* literal, nsString* string);
static PRBool stringEqualsString(nsString* one, nsString* other);
static void initializeStatics();
static void releaseStatics();
};

Просмотреть файл

@ -1026,6 +1026,7 @@ nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttribu
case NS_HTML5TREE_BUILDER_B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
case NS_HTML5TREE_BUILDER_FONT: {
reconstructTheActiveFormattingElements();
maybeForgetEarlierDuplicateFormattingElement(elementName->name, attributes);
appendToCurrentNodeAndPushFormattingElementMayFoster(kNameSpaceID_XHTML, elementName, attributes);
attributes = nsnull;
NS_HTML5_BREAK(starttagloop);
@ -2387,13 +2388,6 @@ nsHtml5TreeBuilder::endTag(nsHtml5ElementName* elementName)
}
NS_HTML5_BREAK(endtagloop);
}
case NS_HTML5TREE_BUILDER_A:
case NS_HTML5TREE_BUILDER_B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
case NS_HTML5TREE_BUILDER_FONT:
case NS_HTML5TREE_BUILDER_NOBR: {
adoptionAgencyEndTag(name);
NS_HTML5_BREAK(endtagloop);
}
case NS_HTML5TREE_BUILDER_OBJECT:
case NS_HTML5TREE_BUILDER_MARQUEE_OR_APPLET: {
eltPos = findLastInScope(name);
@ -2444,6 +2438,14 @@ nsHtml5TreeBuilder::endTag(nsHtml5ElementName* elementName)
} else {
}
}
case NS_HTML5TREE_BUILDER_A:
case NS_HTML5TREE_BUILDER_B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
case NS_HTML5TREE_BUILDER_FONT:
case NS_HTML5TREE_BUILDER_NOBR: {
if (adoptionAgencyEndTag(name)) {
NS_HTML5_BREAK(endtagloop);
}
}
default: {
if (isCurrent(name)) {
pop();
@ -3172,10 +3174,10 @@ nsHtml5TreeBuilder::removeFromListOfActiveFormattingElements(PRInt32 pos)
listPtr--;
}
void
PRBool
nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
{
for (PRInt32 i = 0; i < NS_HTML5TREE_BUILDER_AAA_MAX_ITERATIONS; ++i) {
for (PRInt32 i = 0; i < 8; ++i) {
PRInt32 formattingEltListPos = listPtr;
while (formattingEltListPos > -1) {
nsHtml5StackNode* listNode = listOfActiveFormattingElements[formattingEltListPos];
@ -3188,8 +3190,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
formattingEltListPos--;
}
if (formattingEltListPos == -1) {
return;
return PR_FALSE;
}
nsHtml5StackNode* formattingElt = listOfActiveFormattingElements[formattingEltListPos];
PRInt32 formattingEltStackPos = currentPtr;
@ -3206,11 +3207,11 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
if (formattingEltStackPos == -1) {
removeFromListOfActiveFormattingElements(formattingEltListPos);
return;
return PR_TRUE;
}
if (!inScope) {
return;
return PR_TRUE;
}
PRInt32 furthestBlockPos = formattingEltStackPos + 1;
@ -3226,14 +3227,14 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
pop();
}
removeFromListOfActiveFormattingElements(formattingEltListPos);
return;
return PR_TRUE;
}
nsHtml5StackNode* commonAncestor = stack[formattingEltStackPos - 1];
nsHtml5StackNode* furthestBlock = stack[furthestBlockPos];
PRInt32 bookmark = formattingEltListPos;
PRInt32 nodePos = furthestBlockPos;
nsHtml5StackNode* lastNode = furthestBlock;
for (PRInt32 j = 0; j < NS_HTML5TREE_BUILDER_AAA_MAX_ITERATIONS; ++j) {
for (PRInt32 j = 0; j < 3; ++j) {
nodePos--;
nsHtml5StackNode* node = stack[nodePos];
PRInt32 nodeListPos = findInListOfActiveFormattingElements(node);
@ -3287,6 +3288,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsIAtom* name)
insertIntoStack(formattingClone, furthestBlockPos);
;
}
return PR_TRUE;
}
void
@ -3340,6 +3342,26 @@ nsHtml5TreeBuilder::findInListOfActiveFormattingElementsContainsBetweenEndAndLas
return -1;
}
void
nsHtml5TreeBuilder::maybeForgetEarlierDuplicateFormattingElement(nsIAtom* name, nsHtml5HtmlAttributes* attributes)
{
PRInt32 candidate = -1;
PRInt32 count = 0;
for (PRInt32 i = listPtr; i >= 0; i--) {
nsHtml5StackNode* node = listOfActiveFormattingElements[i];
if (!node) {
break;
}
if (node->name == name && node->attributes->equalsAnother(attributes)) {
candidate = i;
++count;
}
}
if (count >= 3) {
removeFromListOfActiveFormattingElements(candidate);
}
}
PRInt32
nsHtml5TreeBuilder::findLastOrRoot(nsIAtom* name)
{

Просмотреть файл

@ -152,11 +152,12 @@ class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState
void removeFromStack(PRInt32 pos);
void removeFromStack(nsHtml5StackNode* node);
void removeFromListOfActiveFormattingElements(PRInt32 pos);
void adoptionAgencyEndTag(nsIAtom* name);
PRBool adoptionAgencyEndTag(nsIAtom* name);
void insertIntoStack(nsHtml5StackNode* node, PRInt32 position);
void insertIntoListOfActiveFormattingElements(nsHtml5StackNode* formattingClone, PRInt32 bookmark);
PRInt32 findInListOfActiveFormattingElements(nsHtml5StackNode* node);
PRInt32 findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker(nsIAtom* name);
void maybeForgetEarlierDuplicateFormattingElement(nsIAtom* name, nsHtml5HtmlAttributes* attributes);
PRInt32 findLastOrRoot(nsIAtom* name);
PRInt32 findLastOrRoot(PRInt32 group);
PRBool addAttributesToBody(nsHtml5HtmlAttributes* attributes);
@ -345,7 +346,6 @@ class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState
#define NS_HTML5TREE_BUILDER_CHARSET_DOUBLE_QUOTED 10
#define NS_HTML5TREE_BUILDER_CHARSET_UNQUOTED 11
#define NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK PR_INT32_MAX
#define NS_HTML5TREE_BUILDER_AAA_MAX_ITERATIONS 10
#endif

Просмотреть файл

@ -7,4 +7,5 @@
var html5Exceptions = {
"<!doctype html><keygen><frameset>" : true, // Bug 101019
"<select><keygen>" : true, // Bug 101019
"<math><mi><div><object><div><span></span></div></object></div></mi><mi>" : true, // Bug 606925
}

Просмотреть файл

@ -853,11 +853,50 @@ Line: 1 Col: 50 Expected closing tag. Unexpected end of file.
| <i>
| <i>
| <i>
| <i>
| <div>
| <b>
| <div>
| <b>
| "X"
| "TEST"
#data
<p><font size=4><font color=red><font size=4><font size=4><font size=4><font size=4><font size=4><font color=red><p>X
#errors
3: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
116: Unclosed elements.
117: End of file seen and there were open elements.
#document
| <html>
| <head>
| <body>
| <p>
| <font>
| size="4"
| <font>
| color="red"
| <font>
| size="4"
| <font>
| size="4"
| <font>
| size="4"
| <font>
| size="4"
| <font>
| size="4"
| <font>
| color="red"
| <p>
| <font>
| color="red"
| <font>
| size="4"
| <font>
| size="4"
| <font>
| size="4"
| <font>
| color="red"
| "X"
| "TEST"
#data

Просмотреть файл

@ -573,7 +573,7 @@
| <object>
| <div>
| <span>
| <mi>
| <math mi>
#data
<math><mi><svg><foreignObject><div><div></div></div></foreignObject></svg></mi><mi>