| 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 |