Index: lib/parser.dart |
diff --git a/lib/parser.dart b/lib/parser.dart |
index 113a1dd498990b6f230a6bd9c58969a7c52e5974..dc95941dff632a9e239f12c39b5dba7566a2dd3f 100644 |
--- a/lib/parser.dart |
+++ b/lib/parser.dart |
@@ -637,6 +637,7 @@ class Phase { |
} |
// XXX Need a check here to see if the first start tag token emitted is |
// this token... If it's not, invoke parser.parseError(). |
+ tree.openElements[0].sourceSpan = token.span; |
token.data.forEach((attr, value) { |
tree.openElements[0].attributes.putIfAbsent(attr, () => value); |
}); |
@@ -649,11 +650,15 @@ class Phase { |
} |
/// Helper method for popping openElements. |
- void popOpenElementsUntil(String name) { |
+ void popOpenElementsUntil(EndTagToken token) { |
+ String name = token.name; |
var node = tree.openElements.removeLast(); |
while (node.localName != name) { |
node = tree.openElements.removeLast(); |
} |
+ if (node != null) { |
+ node.endSourceSpan = token.span; |
+ } |
} |
} |
@@ -1039,6 +1044,7 @@ class InHeadPhase extends Phase { |
void endTagHead(EndTagToken token) { |
var node = parser.tree.openElements.removeLast(); |
assert(node.localName == "head"); |
+ node.endSourceSpan = token.span; |
parser.phase = parser._afterHeadPhase; |
} |
@@ -1859,7 +1865,7 @@ class InBodyPhase extends Phase { |
if (tree.openElements.last.localName != "p") { |
parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); |
} |
- popOpenElementsUntil("p"); |
+ popOpenElementsUntil(token); |
} |
} |
@@ -1867,7 +1873,9 @@ class InBodyPhase extends Phase { |
if (!tree.elementInScope("body")) { |
parser.parseError(token.span, 'undefined-error'); |
return; |
- } else if (tree.openElements.last.localName != "body") { |
+ } else if (tree.openElements.last.localName == "body") { |
+ tree.openElements.last.endSourceSpan = token.span; |
+ } else { |
for (Element node in slice(tree.openElements, 2)) { |
switch (node.localName) { |
case "dd": |
@@ -1921,7 +1929,7 @@ class InBodyPhase extends Phase { |
parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); |
} |
if (inScope) { |
- popOpenElementsUntil(token.name); |
+ popOpenElementsUntil(token); |
} |
} |
@@ -1937,6 +1945,7 @@ class InBodyPhase extends Phase { |
token.span, "end-tag-too-early-ignored", {"name": "form"}); |
} |
tree.openElements.remove(node); |
+ node.endSourceSpan = token.span; |
} |
} |
@@ -1955,7 +1964,7 @@ class InBodyPhase extends Phase { |
parser.parseError( |
token.span, "end-tag-too-early", {"name": token.name}); |
} |
- popOpenElementsUntil(token.name); |
+ popOpenElementsUntil(token); |
} |
} |
@@ -1972,10 +1981,13 @@ class InBodyPhase extends Phase { |
for (var item in headingElements) { |
if (tree.elementInScope(item)) { |
- var node = tree.openElements.removeLast(); |
+ Element node = tree.openElements.removeLast(); |
while (!headingElements.contains(node.localName)) { |
node = tree.openElements.removeLast(); |
} |
+ if (node != null) { |
+ node.endSourceSpan = token.span; |
+ } |
break; |
} |
} |
@@ -2027,10 +2039,13 @@ class InBodyPhase extends Phase { |
} |
// Step 3 |
if (furthestBlock == null) { |
- var element = tree.openElements.removeLast(); |
+ Element element = tree.openElements.removeLast(); |
while (element != formattingElement) { |
element = tree.openElements.removeLast(); |
} |
+ if (element != null) { |
+ element.endSourceSpan = token.span; |
+ } |
tree.activeFormattingElements.remove(element); |
return; |
} |
@@ -2138,7 +2153,7 @@ class InBodyPhase extends Phase { |
parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); |
} |
if (tree.elementInScope(token.name)) { |
- popOpenElementsUntil(token.name); |
+ popOpenElementsUntil(token); |
tree.clearActiveFormattingElements(); |
} |
} |
@@ -2162,6 +2177,7 @@ class InBodyPhase extends Phase { |
token.span, "unexpected-end-tag", {"name": token.name}); |
} |
while (tree.openElements.removeLast() != node); |
+ node.endSourceSpan = token.span; |
break; |
} else { |
if (specialElements.contains(getElementNameTuple(node))) { |
@@ -2408,7 +2424,8 @@ class InTablePhase extends Phase { |
while (tree.openElements.last.localName != "table") { |
tree.openElements.removeLast(); |
} |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
parser.resetInsertionMode(); |
} else { |
// innerHTML case |
@@ -2585,7 +2602,8 @@ class InCaptionPhase extends Phase { |
while (tree.openElements.last.localName != "caption") { |
tree.openElements.removeLast(); |
} |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
tree.clearActiveFormattingElements(); |
parser.phase = parser._inTablePhase; |
} else { |
@@ -2678,7 +2696,8 @@ class InColumnGroupPhase extends Phase { |
assert(parser.innerHTMLMode); |
parser.parseError(token.span, "undefined-error"); |
} else { |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
parser.phase = parser._inTablePhase; |
} |
} |
@@ -2790,7 +2809,8 @@ class InTableBodyPhase extends Phase { |
void endTagTableRowGroup(EndTagToken token) { |
if (tree.elementInScope(token.name, variant: "table")) { |
clearStackToTableBodyContext(); |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
parser.phase = parser._inTablePhase; |
} else { |
parser.parseError( |
@@ -2924,7 +2944,8 @@ class InRowPhase extends Phase { |
void endTagTr(EndTagToken token) { |
if (!ignoreEndTagTr()) { |
clearStackToTableRowContext(); |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
parser.phase = parser._inTableBodyPhase; |
} else { |
// innerHTML case |
@@ -3048,9 +3069,10 @@ class InCellPhase extends Phase { |
if (tree.openElements.last.localName != token.name) { |
parser.parseError( |
token.span, "unexpected-cell-end-tag", {"name": token.name}); |
- popOpenElementsUntil(token.name); |
+ popOpenElementsUntil(token); |
} else { |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
} |
tree.clearActiveFormattingElements(); |
parser.phase = parser._inRowPhase; |
@@ -3181,7 +3203,8 @@ class InSelectPhase extends Phase { |
void endTagOption(EndTagToken token) { |
if (tree.openElements.last.localName == "option") { |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
} else { |
parser.parseError( |
token.span, "unexpected-end-tag-in-select", {"name": "option"}); |
@@ -3197,7 +3220,8 @@ class InSelectPhase extends Phase { |
} |
// It also closes </optgroup> |
if (tree.openElements.last.localName == "optgroup") { |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
// But nothing else |
} else { |
parser.parseError( |
@@ -3207,7 +3231,7 @@ class InSelectPhase extends Phase { |
void endTagSelect(EndTagToken token) { |
if (tree.elementInScope("select", variant: "select")) { |
- popOpenElementsUntil("select"); |
+ popOpenElementsUntil(token); |
parser.resetInsertionMode(); |
} else { |
// innerHTML case |
@@ -3512,6 +3536,12 @@ class AfterBodyPhase extends Phase { |
} |
void endTagHtml(Token token) { |
+ for (var node in tree.openElements.reversed) { |
+ if (node.localName == 'html') { |
+ node.endSourceSpan = token.span; |
+ break; |
+ } |
+ } |
if (parser.innerHTMLMode) { |
parser.parseError(token.span, "unexpected-end-tag-after-body-innerhtml"); |
} else { |
@@ -3595,7 +3625,8 @@ class InFramesetPhase extends Phase { |
parser.parseError( |
token.span, "unexpected-frameset-in-frameset-innerhtml"); |
} else { |
- tree.openElements.removeLast(); |
+ var node = tree.openElements.removeLast(); |
+ node.endSourceSpan = token.span; |
} |
if (!parser.innerHTMLMode && |
tree.openElements.last.localName != "frameset") { |