OLD | NEW |
1 /// This library has a parser for HTML5 documents, that lets you parse HTML | 1 /// This library has a parser for HTML5 documents, that lets you parse HTML |
2 /// easily from a script or server side application: | 2 /// easily from a script or server side application: |
3 /// | 3 /// |
4 /// import 'package:html/parser.dart' show parse; | 4 /// import 'package:html/parser.dart' show parse; |
5 /// import 'package:html/dom.dart'; | 5 /// import 'package:html/dom.dart'; |
6 /// main() { | 6 /// main() { |
7 /// var document = parse( | 7 /// var document = parse( |
8 /// '<body>Hello world! <a href="www.html5rocks.com">HTML5 rocks!'); | 8 /// '<body>Hello world! <a href="www.html5rocks.com">HTML5 rocks!'); |
9 /// print(document.outerHtml); | 9 /// print(document.outerHtml); |
10 /// } | 10 /// } |
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 Token processStartTag(StartTagToken token) { | 630 Token processStartTag(StartTagToken token) { |
631 throw new UnimplementedError(); | 631 throw new UnimplementedError(); |
632 } | 632 } |
633 | 633 |
634 Token startTagHtml(StartTagToken token) { | 634 Token startTagHtml(StartTagToken token) { |
635 if (parser.firstStartTag == false && token.name == "html") { | 635 if (parser.firstStartTag == false && token.name == "html") { |
636 parser.parseError(token.span, "non-html-root"); | 636 parser.parseError(token.span, "non-html-root"); |
637 } | 637 } |
638 // XXX Need a check here to see if the first start tag token emitted is | 638 // XXX Need a check here to see if the first start tag token emitted is |
639 // this token... If it's not, invoke parser.parseError(). | 639 // this token... If it's not, invoke parser.parseError(). |
| 640 tree.openElements[0].sourceSpan = token.span; |
640 token.data.forEach((attr, value) { | 641 token.data.forEach((attr, value) { |
641 tree.openElements[0].attributes.putIfAbsent(attr, () => value); | 642 tree.openElements[0].attributes.putIfAbsent(attr, () => value); |
642 }); | 643 }); |
643 parser.firstStartTag = false; | 644 parser.firstStartTag = false; |
644 return null; | 645 return null; |
645 } | 646 } |
646 | 647 |
647 Token processEndTag(EndTagToken token) { | 648 Token processEndTag(EndTagToken token) { |
648 throw new UnimplementedError(); | 649 throw new UnimplementedError(); |
649 } | 650 } |
650 | 651 |
651 /// Helper method for popping openElements. | 652 /// Helper method for popping openElements. |
652 void popOpenElementsUntil(String name) { | 653 void popOpenElementsUntil(EndTagToken token) { |
| 654 String name = token.name; |
653 var node = tree.openElements.removeLast(); | 655 var node = tree.openElements.removeLast(); |
654 while (node.localName != name) { | 656 while (node.localName != name) { |
655 node = tree.openElements.removeLast(); | 657 node = tree.openElements.removeLast(); |
656 } | 658 } |
| 659 if (node != null) { |
| 660 node.endSourceSpan = token.span; |
| 661 } |
657 } | 662 } |
658 } | 663 } |
659 | 664 |
660 class InitialPhase extends Phase { | 665 class InitialPhase extends Phase { |
661 InitialPhase(parser) : super(parser); | 666 InitialPhase(parser) : super(parser); |
662 | 667 |
663 Token processSpaceCharacters(SpaceCharactersToken token) { | 668 Token processSpaceCharacters(SpaceCharactersToken token) { |
664 return null; | 669 return null; |
665 } | 670 } |
666 | 671 |
(...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1032 } | 1037 } |
1033 | 1038 |
1034 Token startTagOther(StartTagToken token) { | 1039 Token startTagOther(StartTagToken token) { |
1035 anythingElse(); | 1040 anythingElse(); |
1036 return token; | 1041 return token; |
1037 } | 1042 } |
1038 | 1043 |
1039 void endTagHead(EndTagToken token) { | 1044 void endTagHead(EndTagToken token) { |
1040 var node = parser.tree.openElements.removeLast(); | 1045 var node = parser.tree.openElements.removeLast(); |
1041 assert(node.localName == "head"); | 1046 assert(node.localName == "head"); |
| 1047 node.endSourceSpan = token.span; |
1042 parser.phase = parser._afterHeadPhase; | 1048 parser.phase = parser._afterHeadPhase; |
1043 } | 1049 } |
1044 | 1050 |
1045 Token endTagHtmlBodyBr(EndTagToken token) { | 1051 Token endTagHtmlBodyBr(EndTagToken token) { |
1046 anythingElse(); | 1052 anythingElse(); |
1047 return token; | 1053 return token; |
1048 } | 1054 } |
1049 | 1055 |
1050 void endTagOther(EndTagToken token) { | 1056 void endTagOther(EndTagToken token) { |
1051 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); | 1057 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); |
(...skipping 800 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1852 void endTagP(EndTagToken token) { | 1858 void endTagP(EndTagToken token) { |
1853 if (!tree.elementInScope("p", variant: "button")) { | 1859 if (!tree.elementInScope("p", variant: "button")) { |
1854 startTagCloseP(new StartTagToken("p", data: {})); | 1860 startTagCloseP(new StartTagToken("p", data: {})); |
1855 parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); | 1861 parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); |
1856 endTagP(new EndTagToken("p")); | 1862 endTagP(new EndTagToken("p")); |
1857 } else { | 1863 } else { |
1858 tree.generateImpliedEndTags("p"); | 1864 tree.generateImpliedEndTags("p"); |
1859 if (tree.openElements.last.localName != "p") { | 1865 if (tree.openElements.last.localName != "p") { |
1860 parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); | 1866 parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); |
1861 } | 1867 } |
1862 popOpenElementsUntil("p"); | 1868 popOpenElementsUntil(token); |
1863 } | 1869 } |
1864 } | 1870 } |
1865 | 1871 |
1866 void endTagBody(EndTagToken token) { | 1872 void endTagBody(EndTagToken token) { |
1867 if (!tree.elementInScope("body")) { | 1873 if (!tree.elementInScope("body")) { |
1868 parser.parseError(token.span, 'undefined-error'); | 1874 parser.parseError(token.span, 'undefined-error'); |
1869 return; | 1875 return; |
1870 } else if (tree.openElements.last.localName != "body") { | 1876 } else if (tree.openElements.last.localName == "body") { |
| 1877 tree.openElements.last.endSourceSpan = token.span; |
| 1878 } else { |
1871 for (Element node in slice(tree.openElements, 2)) { | 1879 for (Element node in slice(tree.openElements, 2)) { |
1872 switch (node.localName) { | 1880 switch (node.localName) { |
1873 case "dd": | 1881 case "dd": |
1874 case "dt": | 1882 case "dt": |
1875 case "li": | 1883 case "li": |
1876 case "optgroup": | 1884 case "optgroup": |
1877 case "option": | 1885 case "option": |
1878 case "p": | 1886 case "p": |
1879 case "rp": | 1887 case "rp": |
1880 case "rt": | 1888 case "rt": |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1914 dropNewline = false; | 1922 dropNewline = false; |
1915 } | 1923 } |
1916 var inScope = tree.elementInScope(token.name); | 1924 var inScope = tree.elementInScope(token.name); |
1917 if (inScope) { | 1925 if (inScope) { |
1918 tree.generateImpliedEndTags(); | 1926 tree.generateImpliedEndTags(); |
1919 } | 1927 } |
1920 if (tree.openElements.last.localName != token.name) { | 1928 if (tree.openElements.last.localName != token.name) { |
1921 parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); | 1929 parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); |
1922 } | 1930 } |
1923 if (inScope) { | 1931 if (inScope) { |
1924 popOpenElementsUntil(token.name); | 1932 popOpenElementsUntil(token); |
1925 } | 1933 } |
1926 } | 1934 } |
1927 | 1935 |
1928 void endTagForm(EndTagToken token) { | 1936 void endTagForm(EndTagToken token) { |
1929 var node = tree.formPointer; | 1937 var node = tree.formPointer; |
1930 tree.formPointer = null; | 1938 tree.formPointer = null; |
1931 if (node == null || !tree.elementInScope(node)) { | 1939 if (node == null || !tree.elementInScope(node)) { |
1932 parser.parseError(token.span, "unexpected-end-tag", {"name": "form"}); | 1940 parser.parseError(token.span, "unexpected-end-tag", {"name": "form"}); |
1933 } else { | 1941 } else { |
1934 tree.generateImpliedEndTags(); | 1942 tree.generateImpliedEndTags(); |
1935 if (tree.openElements.last != node) { | 1943 if (tree.openElements.last != node) { |
1936 parser.parseError( | 1944 parser.parseError( |
1937 token.span, "end-tag-too-early-ignored", {"name": "form"}); | 1945 token.span, "end-tag-too-early-ignored", {"name": "form"}); |
1938 } | 1946 } |
1939 tree.openElements.remove(node); | 1947 tree.openElements.remove(node); |
| 1948 node.endSourceSpan = token.span; |
1940 } | 1949 } |
1941 } | 1950 } |
1942 | 1951 |
1943 void endTagListItem(EndTagToken token) { | 1952 void endTagListItem(EndTagToken token) { |
1944 var variant; | 1953 var variant; |
1945 if (token.name == "li") { | 1954 if (token.name == "li") { |
1946 variant = "list"; | 1955 variant = "list"; |
1947 } else { | 1956 } else { |
1948 variant = null; | 1957 variant = null; |
1949 } | 1958 } |
1950 if (!tree.elementInScope(token.name, variant: variant)) { | 1959 if (!tree.elementInScope(token.name, variant: variant)) { |
1951 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); | 1960 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); |
1952 } else { | 1961 } else { |
1953 tree.generateImpliedEndTags(token.name); | 1962 tree.generateImpliedEndTags(token.name); |
1954 if (tree.openElements.last.localName != token.name) { | 1963 if (tree.openElements.last.localName != token.name) { |
1955 parser.parseError( | 1964 parser.parseError( |
1956 token.span, "end-tag-too-early", {"name": token.name}); | 1965 token.span, "end-tag-too-early", {"name": token.name}); |
1957 } | 1966 } |
1958 popOpenElementsUntil(token.name); | 1967 popOpenElementsUntil(token); |
1959 } | 1968 } |
1960 } | 1969 } |
1961 | 1970 |
1962 void endTagHeading(EndTagToken token) { | 1971 void endTagHeading(EndTagToken token) { |
1963 for (var item in headingElements) { | 1972 for (var item in headingElements) { |
1964 if (tree.elementInScope(item)) { | 1973 if (tree.elementInScope(item)) { |
1965 tree.generateImpliedEndTags(); | 1974 tree.generateImpliedEndTags(); |
1966 break; | 1975 break; |
1967 } | 1976 } |
1968 } | 1977 } |
1969 if (tree.openElements.last.localName != token.name) { | 1978 if (tree.openElements.last.localName != token.name) { |
1970 parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); | 1979 parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); |
1971 } | 1980 } |
1972 | 1981 |
1973 for (var item in headingElements) { | 1982 for (var item in headingElements) { |
1974 if (tree.elementInScope(item)) { | 1983 if (tree.elementInScope(item)) { |
1975 var node = tree.openElements.removeLast(); | 1984 Element node = tree.openElements.removeLast(); |
1976 while (!headingElements.contains(node.localName)) { | 1985 while (!headingElements.contains(node.localName)) { |
1977 node = tree.openElements.removeLast(); | 1986 node = tree.openElements.removeLast(); |
1978 } | 1987 } |
| 1988 if (node != null) { |
| 1989 node.endSourceSpan = token.span; |
| 1990 } |
1979 break; | 1991 break; |
1980 } | 1992 } |
1981 } | 1993 } |
1982 } | 1994 } |
1983 | 1995 |
1984 /// The much-feared adoption agency algorithm. | 1996 /// The much-feared adoption agency algorithm. |
1985 endTagFormatting(EndTagToken token) { | 1997 endTagFormatting(EndTagToken token) { |
1986 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construc
tion.html#adoptionAgency | 1998 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construc
tion.html#adoptionAgency |
1987 // TODO(jmesserly): the comments here don't match the numbered steps in the | 1999 // TODO(jmesserly): the comments here don't match the numbered steps in the |
1988 // updated spec. This needs a pass over it to verify that it still matches. | 2000 // updated spec. This needs a pass over it to verify that it still matches. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2020 var afeIndex = tree.openElements.indexOf(formattingElement); | 2032 var afeIndex = tree.openElements.indexOf(formattingElement); |
2021 Node furthestBlock = null; | 2033 Node furthestBlock = null; |
2022 for (Node element in slice(tree.openElements, afeIndex)) { | 2034 for (Node element in slice(tree.openElements, afeIndex)) { |
2023 if (specialElements.contains(getElementNameTuple(element))) { | 2035 if (specialElements.contains(getElementNameTuple(element))) { |
2024 furthestBlock = element; | 2036 furthestBlock = element; |
2025 break; | 2037 break; |
2026 } | 2038 } |
2027 } | 2039 } |
2028 // Step 3 | 2040 // Step 3 |
2029 if (furthestBlock == null) { | 2041 if (furthestBlock == null) { |
2030 var element = tree.openElements.removeLast(); | 2042 Element element = tree.openElements.removeLast(); |
2031 while (element != formattingElement) { | 2043 while (element != formattingElement) { |
2032 element = tree.openElements.removeLast(); | 2044 element = tree.openElements.removeLast(); |
2033 } | 2045 } |
| 2046 if (element != null) { |
| 2047 element.endSourceSpan = token.span; |
| 2048 } |
2034 tree.activeFormattingElements.remove(element); | 2049 tree.activeFormattingElements.remove(element); |
2035 return; | 2050 return; |
2036 } | 2051 } |
2037 | 2052 |
2038 var commonAncestor = tree.openElements[afeIndex - 1]; | 2053 var commonAncestor = tree.openElements[afeIndex - 1]; |
2039 | 2054 |
2040 // Step 5 | 2055 // Step 5 |
2041 // The bookmark is supposed to help us identify where to reinsert | 2056 // The bookmark is supposed to help us identify where to reinsert |
2042 // nodes in step 12. We have to ensure that we reinsert nodes after | 2057 // nodes in step 12. We have to ensure that we reinsert nodes after |
2043 // the node before the active formatting element. Note the bookmark | 2058 // the node before the active formatting element. Note the bookmark |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2131 } | 2146 } |
2132 | 2147 |
2133 void endTagAppletMarqueeObject(EndTagToken token) { | 2148 void endTagAppletMarqueeObject(EndTagToken token) { |
2134 if (tree.elementInScope(token.name)) { | 2149 if (tree.elementInScope(token.name)) { |
2135 tree.generateImpliedEndTags(); | 2150 tree.generateImpliedEndTags(); |
2136 } | 2151 } |
2137 if (tree.openElements.last.localName != token.name) { | 2152 if (tree.openElements.last.localName != token.name) { |
2138 parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); | 2153 parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); |
2139 } | 2154 } |
2140 if (tree.elementInScope(token.name)) { | 2155 if (tree.elementInScope(token.name)) { |
2141 popOpenElementsUntil(token.name); | 2156 popOpenElementsUntil(token); |
2142 tree.clearActiveFormattingElements(); | 2157 tree.clearActiveFormattingElements(); |
2143 } | 2158 } |
2144 } | 2159 } |
2145 | 2160 |
2146 void endTagBr(EndTagToken token) { | 2161 void endTagBr(EndTagToken token) { |
2147 parser.parseError(token.span, "unexpected-end-tag-treated-as", { | 2162 parser.parseError(token.span, "unexpected-end-tag-treated-as", { |
2148 "originalName": "br", | 2163 "originalName": "br", |
2149 "newName": "br element" | 2164 "newName": "br element" |
2150 }); | 2165 }); |
2151 tree.reconstructActiveFormattingElements(); | 2166 tree.reconstructActiveFormattingElements(); |
2152 tree.insertElement(new StartTagToken("br", data: {})); | 2167 tree.insertElement(new StartTagToken("br", data: {})); |
2153 tree.openElements.removeLast(); | 2168 tree.openElements.removeLast(); |
2154 } | 2169 } |
2155 | 2170 |
2156 void endTagOther(EndTagToken token) { | 2171 void endTagOther(EndTagToken token) { |
2157 for (var node in tree.openElements.reversed) { | 2172 for (var node in tree.openElements.reversed) { |
2158 if (node.localName == token.name) { | 2173 if (node.localName == token.name) { |
2159 tree.generateImpliedEndTags(token.name); | 2174 tree.generateImpliedEndTags(token.name); |
2160 if (tree.openElements.last.localName != token.name) { | 2175 if (tree.openElements.last.localName != token.name) { |
2161 parser.parseError( | 2176 parser.parseError( |
2162 token.span, "unexpected-end-tag", {"name": token.name}); | 2177 token.span, "unexpected-end-tag", {"name": token.name}); |
2163 } | 2178 } |
2164 while (tree.openElements.removeLast() != node); | 2179 while (tree.openElements.removeLast() != node); |
| 2180 node.endSourceSpan = token.span; |
2165 break; | 2181 break; |
2166 } else { | 2182 } else { |
2167 if (specialElements.contains(getElementNameTuple(node))) { | 2183 if (specialElements.contains(getElementNameTuple(node))) { |
2168 parser.parseError( | 2184 parser.parseError( |
2169 token.span, "unexpected-end-tag", {"name": token.name}); | 2185 token.span, "unexpected-end-tag", {"name": token.name}); |
2170 break; | 2186 break; |
2171 } | 2187 } |
2172 } | 2188 } |
2173 } | 2189 } |
2174 } | 2190 } |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2401 var last = tree.openElements.last; | 2417 var last = tree.openElements.last; |
2402 if (last.localName != "table") { | 2418 if (last.localName != "table") { |
2403 parser.parseError(token.span, "end-tag-too-early-named", { | 2419 parser.parseError(token.span, "end-tag-too-early-named", { |
2404 "gotName": "table", | 2420 "gotName": "table", |
2405 "expectedName": last.localName | 2421 "expectedName": last.localName |
2406 }); | 2422 }); |
2407 } | 2423 } |
2408 while (tree.openElements.last.localName != "table") { | 2424 while (tree.openElements.last.localName != "table") { |
2409 tree.openElements.removeLast(); | 2425 tree.openElements.removeLast(); |
2410 } | 2426 } |
2411 tree.openElements.removeLast(); | 2427 var node = tree.openElements.removeLast(); |
| 2428 node.endSourceSpan = token.span; |
2412 parser.resetInsertionMode(); | 2429 parser.resetInsertionMode(); |
2413 } else { | 2430 } else { |
2414 // innerHTML case | 2431 // innerHTML case |
2415 assert(parser.innerHTMLMode); | 2432 assert(parser.innerHTMLMode); |
2416 parser.parseError(token.span, "undefined-error"); | 2433 parser.parseError(token.span, "undefined-error"); |
2417 } | 2434 } |
2418 } | 2435 } |
2419 | 2436 |
2420 void endTagIgnore(EndTagToken token) { | 2437 void endTagIgnore(EndTagToken token) { |
2421 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); | 2438 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2578 tree.generateImpliedEndTags(); | 2595 tree.generateImpliedEndTags(); |
2579 if (tree.openElements.last.localName != "caption") { | 2596 if (tree.openElements.last.localName != "caption") { |
2580 parser.parseError(token.span, "expected-one-end-tag-but-got-another", { | 2597 parser.parseError(token.span, "expected-one-end-tag-but-got-another", { |
2581 "gotName": "caption", | 2598 "gotName": "caption", |
2582 "expectedName": tree.openElements.last.localName | 2599 "expectedName": tree.openElements.last.localName |
2583 }); | 2600 }); |
2584 } | 2601 } |
2585 while (tree.openElements.last.localName != "caption") { | 2602 while (tree.openElements.last.localName != "caption") { |
2586 tree.openElements.removeLast(); | 2603 tree.openElements.removeLast(); |
2587 } | 2604 } |
2588 tree.openElements.removeLast(); | 2605 var node = tree.openElements.removeLast(); |
| 2606 node.endSourceSpan = token.span; |
2589 tree.clearActiveFormattingElements(); | 2607 tree.clearActiveFormattingElements(); |
2590 parser.phase = parser._inTablePhase; | 2608 parser.phase = parser._inTablePhase; |
2591 } else { | 2609 } else { |
2592 // innerHTML case | 2610 // innerHTML case |
2593 assert(parser.innerHTMLMode); | 2611 assert(parser.innerHTMLMode); |
2594 parser.parseError(token.span, "undefined-error"); | 2612 parser.parseError(token.span, "undefined-error"); |
2595 } | 2613 } |
2596 } | 2614 } |
2597 | 2615 |
2598 Token endTagTable(EndTagToken token) { | 2616 Token endTagTable(EndTagToken token) { |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2671 endTagColgroup(new EndTagToken("colgroup")); | 2689 endTagColgroup(new EndTagToken("colgroup")); |
2672 return ignoreEndTag ? null : token; | 2690 return ignoreEndTag ? null : token; |
2673 } | 2691 } |
2674 | 2692 |
2675 void endTagColgroup(EndTagToken token) { | 2693 void endTagColgroup(EndTagToken token) { |
2676 if (ignoreEndTagColgroup()) { | 2694 if (ignoreEndTagColgroup()) { |
2677 // innerHTML case | 2695 // innerHTML case |
2678 assert(parser.innerHTMLMode); | 2696 assert(parser.innerHTMLMode); |
2679 parser.parseError(token.span, "undefined-error"); | 2697 parser.parseError(token.span, "undefined-error"); |
2680 } else { | 2698 } else { |
2681 tree.openElements.removeLast(); | 2699 var node = tree.openElements.removeLast(); |
| 2700 node.endSourceSpan = token.span; |
2682 parser.phase = parser._inTablePhase; | 2701 parser.phase = parser._inTablePhase; |
2683 } | 2702 } |
2684 } | 2703 } |
2685 | 2704 |
2686 void endTagCol(EndTagToken token) { | 2705 void endTagCol(EndTagToken token) { |
2687 parser.parseError(token.span, "no-end-tag", {"name": "col"}); | 2706 parser.parseError(token.span, "no-end-tag", {"name": "col"}); |
2688 } | 2707 } |
2689 | 2708 |
2690 Token endTagOther(EndTagToken token) { | 2709 Token endTagOther(EndTagToken token) { |
2691 var ignoreEndTag = ignoreEndTagColgroup(); | 2710 var ignoreEndTag = ignoreEndTagColgroup(); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2783 | 2802 |
2784 Token startTagTableOther(token) => endTagTable(token); | 2803 Token startTagTableOther(token) => endTagTable(token); |
2785 | 2804 |
2786 Token startTagOther(StartTagToken token) { | 2805 Token startTagOther(StartTagToken token) { |
2787 return parser._inTablePhase.processStartTag(token); | 2806 return parser._inTablePhase.processStartTag(token); |
2788 } | 2807 } |
2789 | 2808 |
2790 void endTagTableRowGroup(EndTagToken token) { | 2809 void endTagTableRowGroup(EndTagToken token) { |
2791 if (tree.elementInScope(token.name, variant: "table")) { | 2810 if (tree.elementInScope(token.name, variant: "table")) { |
2792 clearStackToTableBodyContext(); | 2811 clearStackToTableBodyContext(); |
2793 tree.openElements.removeLast(); | 2812 var node = tree.openElements.removeLast(); |
| 2813 node.endSourceSpan = token.span; |
2794 parser.phase = parser._inTablePhase; | 2814 parser.phase = parser._inTablePhase; |
2795 } else { | 2815 } else { |
2796 parser.parseError( | 2816 parser.parseError( |
2797 token.span, "unexpected-end-tag-in-table-body", {"name": token.name}); | 2817 token.span, "unexpected-end-tag-in-table-body", {"name": token.name}); |
2798 } | 2818 } |
2799 } | 2819 } |
2800 | 2820 |
2801 Token endTagTable(TagToken token) { | 2821 Token endTagTable(TagToken token) { |
2802 // XXX AT Any ideas on how to share this with endTagTable? | 2822 // XXX AT Any ideas on how to share this with endTagTable? |
2803 if (tree.elementInScope("tbody", variant: "table") || | 2823 if (tree.elementInScope("tbody", variant: "table") || |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2917 return ignoreEndTag ? null : token; | 2937 return ignoreEndTag ? null : token; |
2918 } | 2938 } |
2919 | 2939 |
2920 Token startTagOther(StartTagToken token) { | 2940 Token startTagOther(StartTagToken token) { |
2921 return parser._inTablePhase.processStartTag(token); | 2941 return parser._inTablePhase.processStartTag(token); |
2922 } | 2942 } |
2923 | 2943 |
2924 void endTagTr(EndTagToken token) { | 2944 void endTagTr(EndTagToken token) { |
2925 if (!ignoreEndTagTr()) { | 2945 if (!ignoreEndTagTr()) { |
2926 clearStackToTableRowContext(); | 2946 clearStackToTableRowContext(); |
2927 tree.openElements.removeLast(); | 2947 var node = tree.openElements.removeLast(); |
| 2948 node.endSourceSpan = token.span; |
2928 parser.phase = parser._inTableBodyPhase; | 2949 parser.phase = parser._inTableBodyPhase; |
2929 } else { | 2950 } else { |
2930 // innerHTML case | 2951 // innerHTML case |
2931 assert(parser.innerHTMLMode); | 2952 assert(parser.innerHTMLMode); |
2932 parser.parseError(token.span, "undefined-error"); | 2953 parser.parseError(token.span, "undefined-error"); |
2933 } | 2954 } |
2934 } | 2955 } |
2935 | 2956 |
2936 Token endTagTable(EndTagToken token) { | 2957 Token endTagTable(EndTagToken token) { |
2937 var ignoreEndTag = ignoreEndTagTr(); | 2958 var ignoreEndTag = ignoreEndTagTr(); |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3041 Token startTagOther(StartTagToken token) { | 3062 Token startTagOther(StartTagToken token) { |
3042 return parser._inBodyPhase.processStartTag(token); | 3063 return parser._inBodyPhase.processStartTag(token); |
3043 } | 3064 } |
3044 | 3065 |
3045 void endTagTableCell(EndTagToken token) { | 3066 void endTagTableCell(EndTagToken token) { |
3046 if (tree.elementInScope(token.name, variant: "table")) { | 3067 if (tree.elementInScope(token.name, variant: "table")) { |
3047 tree.generateImpliedEndTags(token.name); | 3068 tree.generateImpliedEndTags(token.name); |
3048 if (tree.openElements.last.localName != token.name) { | 3069 if (tree.openElements.last.localName != token.name) { |
3049 parser.parseError( | 3070 parser.parseError( |
3050 token.span, "unexpected-cell-end-tag", {"name": token.name}); | 3071 token.span, "unexpected-cell-end-tag", {"name": token.name}); |
3051 popOpenElementsUntil(token.name); | 3072 popOpenElementsUntil(token); |
3052 } else { | 3073 } else { |
3053 tree.openElements.removeLast(); | 3074 var node = tree.openElements.removeLast(); |
| 3075 node.endSourceSpan = token.span; |
3054 } | 3076 } |
3055 tree.clearActiveFormattingElements(); | 3077 tree.clearActiveFormattingElements(); |
3056 parser.phase = parser._inRowPhase; | 3078 parser.phase = parser._inRowPhase; |
3057 } else { | 3079 } else { |
3058 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); | 3080 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); |
3059 } | 3081 } |
3060 } | 3082 } |
3061 | 3083 |
3062 void endTagIgnore(EndTagToken token) { | 3084 void endTagIgnore(EndTagToken token) { |
3063 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); | 3085 parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3174 } | 3196 } |
3175 | 3197 |
3176 Token startTagOther(StartTagToken token) { | 3198 Token startTagOther(StartTagToken token) { |
3177 parser.parseError( | 3199 parser.parseError( |
3178 token.span, "unexpected-start-tag-in-select", {"name": token.name}); | 3200 token.span, "unexpected-start-tag-in-select", {"name": token.name}); |
3179 return null; | 3201 return null; |
3180 } | 3202 } |
3181 | 3203 |
3182 void endTagOption(EndTagToken token) { | 3204 void endTagOption(EndTagToken token) { |
3183 if (tree.openElements.last.localName == "option") { | 3205 if (tree.openElements.last.localName == "option") { |
3184 tree.openElements.removeLast(); | 3206 var node = tree.openElements.removeLast(); |
| 3207 node.endSourceSpan = token.span; |
3185 } else { | 3208 } else { |
3186 parser.parseError( | 3209 parser.parseError( |
3187 token.span, "unexpected-end-tag-in-select", {"name": "option"}); | 3210 token.span, "unexpected-end-tag-in-select", {"name": "option"}); |
3188 } | 3211 } |
3189 } | 3212 } |
3190 | 3213 |
3191 void endTagOptgroup(EndTagToken token) { | 3214 void endTagOptgroup(EndTagToken token) { |
3192 // </optgroup> implicitly closes <option> | 3215 // </optgroup> implicitly closes <option> |
3193 if (tree.openElements.last.localName == "option" && | 3216 if (tree.openElements.last.localName == "option" && |
3194 tree.openElements[tree.openElements.length - 2].localName == | 3217 tree.openElements[tree.openElements.length - 2].localName == |
3195 "optgroup") { | 3218 "optgroup") { |
3196 tree.openElements.removeLast(); | 3219 tree.openElements.removeLast(); |
3197 } | 3220 } |
3198 // It also closes </optgroup> | 3221 // It also closes </optgroup> |
3199 if (tree.openElements.last.localName == "optgroup") { | 3222 if (tree.openElements.last.localName == "optgroup") { |
3200 tree.openElements.removeLast(); | 3223 var node = tree.openElements.removeLast(); |
| 3224 node.endSourceSpan = token.span; |
3201 // But nothing else | 3225 // But nothing else |
3202 } else { | 3226 } else { |
3203 parser.parseError( | 3227 parser.parseError( |
3204 token.span, "unexpected-end-tag-in-select", {"name": "optgroup"}); | 3228 token.span, "unexpected-end-tag-in-select", {"name": "optgroup"}); |
3205 } | 3229 } |
3206 } | 3230 } |
3207 | 3231 |
3208 void endTagSelect(EndTagToken token) { | 3232 void endTagSelect(EndTagToken token) { |
3209 if (tree.elementInScope("select", variant: "select")) { | 3233 if (tree.elementInScope("select", variant: "select")) { |
3210 popOpenElementsUntil("select"); | 3234 popOpenElementsUntil(token); |
3211 parser.resetInsertionMode(); | 3235 parser.resetInsertionMode(); |
3212 } else { | 3236 } else { |
3213 // innerHTML case | 3237 // innerHTML case |
3214 assert(parser.innerHTMLMode); | 3238 assert(parser.innerHTMLMode); |
3215 parser.parseError(token.span, "undefined-error"); | 3239 parser.parseError(token.span, "undefined-error"); |
3216 } | 3240 } |
3217 } | 3241 } |
3218 | 3242 |
3219 void endTagOther(EndTagToken token) { | 3243 void endTagOther(EndTagToken token) { |
3220 parser.parseError( | 3244 parser.parseError( |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3505 } | 3529 } |
3506 | 3530 |
3507 Token startTagOther(StartTagToken token) { | 3531 Token startTagOther(StartTagToken token) { |
3508 parser.parseError( | 3532 parser.parseError( |
3509 token.span, "unexpected-start-tag-after-body", {"name": token.name}); | 3533 token.span, "unexpected-start-tag-after-body", {"name": token.name}); |
3510 parser.phase = parser._inBodyPhase; | 3534 parser.phase = parser._inBodyPhase; |
3511 return token; | 3535 return token; |
3512 } | 3536 } |
3513 | 3537 |
3514 void endTagHtml(Token token) { | 3538 void endTagHtml(Token token) { |
| 3539 for (var node in tree.openElements.reversed) { |
| 3540 if (node.localName == 'html') { |
| 3541 node.endSourceSpan = token.span; |
| 3542 break; |
| 3543 } |
| 3544 } |
3515 if (parser.innerHTMLMode) { | 3545 if (parser.innerHTMLMode) { |
3516 parser.parseError(token.span, "unexpected-end-tag-after-body-innerhtml"); | 3546 parser.parseError(token.span, "unexpected-end-tag-after-body-innerhtml"); |
3517 } else { | 3547 } else { |
3518 parser.phase = parser._afterAfterBodyPhase; | 3548 parser.phase = parser._afterAfterBodyPhase; |
3519 } | 3549 } |
3520 } | 3550 } |
3521 | 3551 |
3522 Token endTagOther(EndTagToken token) { | 3552 Token endTagOther(EndTagToken token) { |
3523 parser.parseError( | 3553 parser.parseError( |
3524 token.span, "unexpected-end-tag-after-body", {"name": token.name}); | 3554 token.span, "unexpected-end-tag-after-body", {"name": token.name}); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3588 token.span, "unexpected-start-tag-in-frameset", {"name": token.name}); | 3618 token.span, "unexpected-start-tag-in-frameset", {"name": token.name}); |
3589 return null; | 3619 return null; |
3590 } | 3620 } |
3591 | 3621 |
3592 void endTagFrameset(EndTagToken token) { | 3622 void endTagFrameset(EndTagToken token) { |
3593 if (tree.openElements.last.localName == "html") { | 3623 if (tree.openElements.last.localName == "html") { |
3594 // innerHTML case | 3624 // innerHTML case |
3595 parser.parseError( | 3625 parser.parseError( |
3596 token.span, "unexpected-frameset-in-frameset-innerhtml"); | 3626 token.span, "unexpected-frameset-in-frameset-innerhtml"); |
3597 } else { | 3627 } else { |
3598 tree.openElements.removeLast(); | 3628 var node = tree.openElements.removeLast(); |
| 3629 node.endSourceSpan = token.span; |
3599 } | 3630 } |
3600 if (!parser.innerHTMLMode && | 3631 if (!parser.innerHTMLMode && |
3601 tree.openElements.last.localName != "frameset") { | 3632 tree.openElements.last.localName != "frameset") { |
3602 // If we're not in innerHTML mode and the the current node is not a | 3633 // If we're not in innerHTML mode and the the current node is not a |
3603 // "frameset" element (anymore) then switch. | 3634 // "frameset" element (anymore) then switch. |
3604 parser.phase = parser._afterFramesetPhase; | 3635 parser.phase = parser._afterFramesetPhase; |
3605 } | 3636 } |
3606 } | 3637 } |
3607 | 3638 |
3608 void endTagOther(EndTagToken token) { | 3639 void endTagOther(EndTagToken token) { |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3782 return span.sourceUrl == null ? 'ParserError on $res' : 'On $res'; | 3813 return span.sourceUrl == null ? 'ParserError on $res' : 'On $res'; |
3783 } | 3814 } |
3784 } | 3815 } |
3785 | 3816 |
3786 /// Convenience function to get the pair of namespace and localName. | 3817 /// Convenience function to get the pair of namespace and localName. |
3787 Pair<String, String> getElementNameTuple(Element e) { | 3818 Pair<String, String> getElementNameTuple(Element e) { |
3788 var ns = e.namespaceUri; | 3819 var ns = e.namespaceUri; |
3789 if (ns == null) ns = Namespaces.html; | 3820 if (ns == null) ns = Namespaces.html; |
3790 return new Pair(ns, e.localName); | 3821 return new Pair(ns, e.localName); |
3791 } | 3822 } |
OLD | NEW |