diff --git a/pkgs/html/analysis_options.yaml b/pkgs/html/analysis_options.yaml index 2d3a54d6d..b8dcf936a 100644 --- a/pkgs/html/analysis_options.yaml +++ b/pkgs/html/analysis_options.yaml @@ -1,6 +1,8 @@ include: package:pedantic/analysis_options.yaml analyzer: errors: + # https://github.com/dart-lang/linter/issues/1649 + prefer_collection_literals: ignore unused_element: error unused_import: error unused_local_variable: error diff --git a/pkgs/html/lib/dom.dart b/pkgs/html/lib/dom.dart index 08d1c1240..835d8da92 100644 --- a/pkgs/html/lib/dom.dart +++ b/pkgs/html/lib/dom.dart @@ -36,6 +36,7 @@ class AttributeName implements Comparable { const AttributeName(this.prefix, this.name, this.namespace); + @override String toString() { // Implement: // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments @@ -45,24 +46,26 @@ class AttributeName implements Comparable { return prefix != null ? '$prefix:$name' : name; } + @override int get hashCode { - int h = prefix.hashCode; + var h = prefix.hashCode; h = 37 * (h & 0x1FFFFF) + name.hashCode; h = 37 * (h & 0x1FFFFF) + namespace.hashCode; return h & 0x3FFFFFFF; } + @override int compareTo(other) { // Not sure about this sort order if (other is! AttributeName) return 1; - int cmp = (prefix != null ? prefix : "") - .compareTo((other.prefix != null ? other.prefix : "")); + var cmp = (prefix ?? '').compareTo((other.prefix ?? '')); if (cmp != 0) return cmp; cmp = name.compareTo(other.name); if (cmp != 0) return cmp; return namespace.compareTo(other.namespace); } + @override bool operator ==(x) { if (x is! AttributeName) return false; return prefix == x.prefix && name == x.name && namespace == x.namespace; @@ -184,12 +187,7 @@ abstract class Node { return _attributeValueSpans; } - List get children { - if (_elements == null) { - _elements = FilteredElementList(this); - } - return _elements; - } + List get children => _elements ??= FilteredElementList(this); /// Returns a copy of this node. /// @@ -223,7 +221,7 @@ abstract class Node { void _addOuterHtml(StringBuffer str); void _addInnerHtml(StringBuffer str) { - for (Node child in nodes) { + for (var child in nodes) { child._addOuterHtml(str); } } @@ -301,7 +299,7 @@ abstract class Node { } } - _clone(Node shallowClone, bool deep) { + Node _clone(Node shallowClone, bool deep) { if (deep) { for (var child in nodes) { shallowClone.append(child.clone(true)); @@ -316,6 +314,7 @@ class Document extends Node Document() : super._(); factory Document.html(String html) => parse(html); + @override int get nodeType => Node.DOCUMENT_NODE; // TODO(jmesserly): optmize this if needed @@ -331,10 +330,13 @@ class Document extends Node // to dom_parsing, where we keep other custom APIs? String get outerHtml => _outerHtml; - String toString() => "#document"; + @override + String toString() => '#document'; + @override void _addOuterHtml(StringBuffer str) => _addInnerHtml(str); + @override Document clone(bool deep) => _clone(Document(), deep); Element createElement(String tag) => Element.tag(tag); @@ -353,6 +355,7 @@ class DocumentFragment extends Node with _ParentNode, _NonElementParentNode { DocumentFragment() : super._(); factory DocumentFragment.html(String html) => parseFragment(html); + @override int get nodeType => Node.DOCUMENT_FRAGMENT_NODE; /// Returns a fragment of HTML or XML that represents the element and its @@ -363,13 +366,18 @@ class DocumentFragment extends Node with _ParentNode, _NonElementParentNode { // to dom_parsing, where we keep other custom APIs? String get outerHtml => _outerHtml; - String toString() => "#document-fragment"; + @override + String toString() => '#document-fragment'; + @override DocumentFragment clone(bool deep) => _clone(DocumentFragment(), deep); + @override void _addOuterHtml(StringBuffer str) => _addInnerHtml(str); + @override String get text => _getText(this); + @override set text(String value) => _setText(this, value); } @@ -380,24 +388,28 @@ class DocumentType extends Node { DocumentType(this.name, this.publicId, this.systemId) : super._(); + @override int get nodeType => Node.DOCUMENT_TYPE_NODE; + @override String toString() { if (publicId != null || systemId != null) { // TODO(jmesserly): the html5 serialization spec does not add these. But // it seems useful, and the parser can handle it, so for now keeping it. - var pid = publicId != null ? publicId : ''; - var sid = systemId != null ? systemId : ''; + var pid = publicId ?? ''; + var sid = systemId ?? ''; return ''; } else { return ''; } } + @override void _addOuterHtml(StringBuffer str) { str.write(toString()); } + @override DocumentType clone(bool deep) => DocumentType(name, publicId, systemId); } @@ -408,20 +420,24 @@ class Text extends Node { dynamic _data; Text(String data) - : _data = data != null ? data : '', + : _data = data ?? '', super._(); + @override int get nodeType => Node.TEXT_NODE; String get data => _data = _data.toString(); set data(String value) { - _data = value != null ? value : ''; + _data = value ?? ''; } + @override String toString() => '"$data"'; + @override void _addOuterHtml(StringBuffer str) => writeTextNodeAsHtml(str, this); + @override Text clone(bool deep) => Text(data); void appendData(String data) { @@ -430,7 +446,9 @@ class Text extends Node { sb.write(data); } + @override String get text => data; + @override set text(String value) { data = value; } @@ -484,7 +502,7 @@ class Element extends Node with _ParentNode, _ElementAndDocument { // 2) Verify that the html does not contain leading or trailing text nodes. // 3) Verify that the html does not contain both and tags. // 4) Detatch the created element from its dummy parent. - String parentTag = 'div'; + var parentTag = 'div'; String tag; final match = _startTagRegexp.firstMatch(html); if (match != null) { @@ -509,13 +527,14 @@ class Element extends Node with _ParentNode, _ElementAndDocument { return element; } + @override int get nodeType => Node.ELEMENT_NODE; // TODO(jmesserly): we can make this faster Element get previousElementSibling { if (parentNode == null) return null; var siblings = parentNode.nodes; - for (int i = siblings.indexOf(this) - 1; i >= 0; i--) { + for (var i = siblings.indexOf(this) - 1; i >= 0; i--) { var s = siblings[i]; if (s is Element) return s; } @@ -525,19 +544,22 @@ class Element extends Node with _ParentNode, _ElementAndDocument { Element get nextElementSibling { if (parentNode == null) return null; var siblings = parentNode.nodes; - for (int i = siblings.indexOf(this) + 1; i < siblings.length; i++) { + for (var i = siblings.indexOf(this) + 1; i < siblings.length; i++) { var s = siblings[i]; if (s is Element) return s; } return null; } + @override String toString() { var prefix = Namespaces.getPrefix(namespaceUri); return "<${prefix == null ? '' : '$prefix '}$localName>"; } + @override String get text => _getText(this); + @override set text(String value) => _setText(this, value); /// Returns a fragment of HTML or XML that represents the element and its @@ -557,6 +579,7 @@ class Element extends Node with _ParentNode, _ElementAndDocument { nodes.addAll(parseFragment(value, container: localName).nodes); } + @override void _addOuterHtml(StringBuffer str) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments // Element is the most complicated one. @@ -611,6 +634,7 @@ class Element extends Node with _ParentNode, _ElementAndDocument { return prefix == null ? '' : '$prefix:'; } + @override Element clone(bool deep) { var result = Element._(localName, namespaceUri) ..attributes = LinkedHashMap.from(attributes); @@ -620,7 +644,7 @@ class Element extends Node with _ParentNode, _ElementAndDocument { // http://dom.spec.whatwg.org/#dom-element-id String get id { var result = attributes['id']; - return result != null ? result : ''; + return result ?? ''; } set id(String value) { @@ -630,7 +654,7 @@ class Element extends Node with _ParentNode, _ElementAndDocument { // http://dom.spec.whatwg.org/#dom-element-classname String get className { var result = attributes['class']; - return result != null ? result : ''; + return result ?? ''; } set className(String value) { @@ -653,17 +677,23 @@ class Comment extends Node { Comment(this.data) : super._(); + @override int get nodeType => Node.COMMENT_NODE; - String toString() => ""; + @override + String toString() => ''; + @override void _addOuterHtml(StringBuffer str) { - str.write(""); + str.write(''); } + @override Comment clone(bool deep) => Comment(data); + @override String get text => data; + @override set text(String value) { data = value; } @@ -687,6 +717,7 @@ class NodeList extends ListProxy { return node; } + @override void add(Node value) { if (value is DocumentFragment) { addAll(value.nodes); @@ -697,6 +728,7 @@ class NodeList extends ListProxy { void addLast(Node value) => add(value); + @override void addAll(Iterable collection) { // Note: we need to be careful if collection is another NodeList. // In particular: @@ -712,6 +744,7 @@ class NodeList extends ListProxy { super.addAll(list); } + @override void insert(int index, Node value) { if (value is DocumentFragment) { insertAll(index, value.nodes); @@ -720,10 +753,13 @@ class NodeList extends ListProxy { } } + @override Node removeLast() => super.removeLast()..parentNode = null; + @override Node removeAt(int i) => super.removeAt(i)..parentNode = null; + @override void clear() { for (var node in this) { node.parentNode = null; @@ -731,6 +767,7 @@ class NodeList extends ListProxy { super.clear(); } + @override void operator []=(int index, Node value) { if (value is DocumentFragment) { removeAt(index); @@ -743,46 +780,52 @@ class NodeList extends ListProxy { // TODO(jmesserly): These aren't implemented in DOM _NodeListImpl, see // http://code.google.com/p/dart/issues/detail?id=5371 + @override void setRange(int start, int rangeLength, Iterable from, [int startFrom = 0]) { - List fromVar = from as List; + var fromVar = from as List; if (fromVar is NodeList) { // Note: this is presumed to make a copy fromVar = fromVar.sublist(startFrom, startFrom + rangeLength); } // Note: see comment in [addAll]. We need to be careful about the order of // operations if [from] is also a NodeList. - for (int i = rangeLength - 1; i >= 0; i--) { + for (var i = rangeLength - 1; i >= 0; i--) { this[start + i] = fromVar[startFrom + i]; } } + @override void replaceRange(int start, int end, Iterable newContents) { removeRange(start, end); insertAll(start, newContents); } + @override void removeRange(int start, int rangeLength) { - for (int i = start; i < rangeLength; i++) { + for (var i = start; i < rangeLength; i++) { this[i].parentNode = null; } super.removeRange(start, rangeLength); } - void removeWhere(bool test(Node e)) { + @override + void removeWhere(bool Function(Node) test) { for (var node in where(test)) { node.parentNode = null; } super.removeWhere(test); } - void retainWhere(bool test(Node e)) { + @override + void retainWhere(bool Function(Node) test) { for (var node in where((n) => !test(n))) { node.parentNode = null; } super.retainWhere(test); } + @override void insertAll(int index, Iterable collection) { // Note: we need to be careful how we copy nodes. See note in addAll. var list = _flattenDocFragments(collection); @@ -832,70 +875,85 @@ class FilteredElementList extends IterableBase // forEach, every, any, ... could directly work on the _childNodes. List get _filtered => _childNodes.whereType().toList(); - void forEach(void f(Element element)) { + @override + void forEach(void Function(Element) f) { _filtered.forEach(f); } + @override void operator []=(int index, Element value) { this[index].replaceWith(value); } + @override set length(int newLength) { final len = length; if (newLength >= len) { return; } else if (newLength < 0) { - throw ArgumentError("Invalid list length"); + throw ArgumentError('Invalid list length'); } removeRange(newLength, len); } - String join([String separator = ""]) => _filtered.join(separator); + @override + String join([String separator = '']) => _filtered.join(separator); + @override void add(Element value) { _childNodes.add(value); } + @override void addAll(Iterable iterable) { - for (Element element in iterable) { + for (var element in iterable) { add(element); } } + @override bool contains(Object element) { return element is Element && _childNodes.contains(element); } + @override Iterable get reversed => _filtered.reversed; - void sort([int compare(Element a, Element b)]) { + @override + void sort([int Function(Element, Element) compare]) { throw UnsupportedError('TODO(jacobr): should we impl?'); } + @override void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { throw UnimplementedError(); } + @override void fillRange(int start, int end, [Element fillValue]) { throw UnimplementedError(); } + @override void replaceRange(int start, int end, Iterable iterable) { throw UnimplementedError(); } + @override void removeRange(int start, int end) { _filtered.sublist(start, end).forEach((el) => el.remove()); } + @override void clear() { // Currently, ElementList#clear clears even non-element nodes, so we follow // that behavior. _childNodes.clear(); } + @override Element removeLast() { final result = last; if (result != null) { @@ -904,28 +962,35 @@ class FilteredElementList extends IterableBase return result; } - Iterable map(T f(Element element)) => _filtered.map(f); - Iterable where(bool f(Element element)) => _filtered.where(f); - Iterable expand(Iterable f(Element element)) => _filtered.expand(f); + @override + Iterable map(T Function(Element) f) => _filtered.map(f); + @override + Iterable where(bool Function(Element) f) => _filtered.where(f); + @override + Iterable expand(Iterable Function(Element) f) => _filtered.expand(f); + @override void insert(int index, Element value) { _childNodes.insert(index, value); } + @override void insertAll(int index, Iterable iterable) { _childNodes.insertAll(index, iterable); } + @override Element removeAt(int index) { final result = this[index]; result.remove(); return result; } + @override bool remove(Object element) { if (element is! Element) return false; - for (int i = 0; i < length; i++) { - Element indexElement = this[i]; + for (var i = 0; i < length; i++) { + var indexElement = this[i]; if (identical(indexElement, element)) { indexElement.remove(); return true; @@ -934,59 +999,82 @@ class FilteredElementList extends IterableBase return false; } - Element reduce(Element combine(Element value, Element element)) { + @override + Element reduce(Element Function(Element, Element) combine) { return _filtered.reduce(combine); } - T fold(T initialValue, T combine(T previousValue, Element element)) { + @override + T fold( + T initialValue, T Function(T previousValue, Element element) combine) { return _filtered.fold(initialValue, combine); } - bool every(bool f(Element element)) => _filtered.every(f); - bool any(bool f(Element element)) => _filtered.any(f); + @override + bool every(bool Function(Element) f) => _filtered.every(f); + @override + bool any(bool Function(Element) f) => _filtered.any(f); + @override List toList({bool growable = true}) => List.from(this, growable: growable); + @override Set toSet() => Set.from(this); - Element firstWhere(bool test(Element value), {Element orElse()}) { + @override + Element firstWhere(bool Function(Element) test, {Element Function() orElse}) { return _filtered.firstWhere(test, orElse: orElse); } - Element lastWhere(bool test(Element value), {Element orElse()}) { + @override + Element lastWhere(bool Function(Element) test, {Element Function() orElse}) { return _filtered.lastWhere(test, orElse: orElse); } - Element singleWhere(bool test(Element value), {Element orElse()}) { + @override + Element singleWhere(bool Function(Element) test, + {Element Function() orElse}) { if (orElse != null) throw UnimplementedError('orElse'); return _filtered.singleWhere(test); } + @override Element elementAt(int index) { return this[index]; } + @override bool get isEmpty => _filtered.isEmpty; + @override int get length => _filtered.length; + @override Element operator [](int index) => _filtered[index]; + @override Iterator get iterator => _filtered.iterator; + @override List sublist(int start, [int end]) => _filtered.sublist(start, end); + @override Iterable getRange(int start, int end) => _filtered.getRange(start, end); // TODO(sigmund): this should be typed Element, but we currently run into a // bug where ListMixin.indexOf() expects Object as the argument. + @override int indexOf(Object element, [int start = 0]) => _filtered.indexOf(element, start); // TODO(sigmund): this should be typed Element, but we currently run into a // bug where ListMixin.lastIndexOf() expects Object as the argument. + @override int lastIndexOf(Object element, [int start]) { - if (start == null) start = length - 1; + start ??= length - 1; return _filtered.lastIndexOf(element, start); } + @override Element get first => _filtered.first; + @override Element get last => _filtered.last; + @override Element get single => _filtered.single; } @@ -1002,9 +1090,11 @@ void _setText(Node node, String value) { class _ConcatTextVisitor extends TreeVisitor { final _str = StringBuffer(); + @override String toString() => _str.toString(); - visitText(Text node) { + @override + void visitText(Text node) { _str.write(node.data); } } diff --git a/pkgs/html/lib/dom_parsing.dart b/pkgs/html/lib/dom_parsing.dart index 27eedb3be..f16018c36 100644 --- a/pkgs/html/lib/dom_parsing.dart +++ b/pkgs/html/lib/dom_parsing.dart @@ -7,7 +7,7 @@ import 'src/constants.dart' show rcdataElements; /// A simple tree visitor for the DOM nodes. class TreeVisitor { - visit(Node node) { + void visit(Node node) { switch (node.nodeType) { case Node.ELEMENT_NODE: return visitElement(node); @@ -26,7 +26,7 @@ class TreeVisitor { } } - visitChildren(Node node) { + void visitChildren(Node node) { // Allow for mutations (remove works) while iterating. for (var child in node.nodes.toList()) { visit(child); @@ -36,20 +36,20 @@ class TreeVisitor { /// The fallback handler if the more specific visit method hasn't been /// overriden. Only use this from a subclass of [TreeVisitor], otherwise /// call [visit] instead. - visitNodeFallback(Node node) => visitChildren(node); + void visitNodeFallback(Node node) => visitChildren(node); - visitDocument(Document node) => visitNodeFallback(node); + void visitDocument(Document node) => visitNodeFallback(node); - visitDocumentType(DocumentType node) => visitNodeFallback(node); + void visitDocumentType(DocumentType node) => visitNodeFallback(node); - visitText(Text node) => visitNodeFallback(node); + void visitText(Text node) => visitNodeFallback(node); // TODO(jmesserly): visit attributes. - visitElement(Element node) => visitNodeFallback(node); + void visitElement(Element node) => visitNodeFallback(node); - visitComment(Comment node) => visitNodeFallback(node); + void visitComment(Comment node) => visitNodeFallback(node); - visitDocumentFragment(DocumentFragment node) => visitNodeFallback(node); + void visitDocumentFragment(DocumentFragment node) => visitNodeFallback(node); } /// Converts the DOM tree into an HTML string with code markup suitable for @@ -67,24 +67,29 @@ class CodeMarkupVisitor extends TreeVisitor { CodeMarkupVisitor() : _str = StringBuffer(); + @override String toString() => _str.toString(); - visitDocument(Document node) { - _str.write("
");
+  @override
+  void visitDocument(Document node) {
+    _str.write('
');
     visitChildren(node);
-    _str.write("
"); + _str.write('
'); } - visitDocumentType(DocumentType node) { + @override + void visitDocumentType(DocumentType node) { _str.write('<!DOCTYPE ${node.name}>' ''); } - visitText(Text node) { + @override + void visitText(Text node) { writeTextNodeAsHtml(_str, node); } - visitElement(Element node) { + @override + void visitElement(Element node) { final tag = node.localName; _str.write('<$tag'); if (node.attributes.isNotEmpty) { @@ -95,16 +100,17 @@ class CodeMarkupVisitor extends TreeVisitor { }); } if (node.nodes.isNotEmpty) { - _str.write(">"); + _str.write('>'); visitChildren(node); } else if (isVoidElement(tag)) { - _str.write(">"); + _str.write('>'); return; } _str.write('</$tag>'); } - visitComment(Comment node) { + @override + void visitComment(Comment node) { var data = htmlSerializeEscape(node.data); _str.write('<!--$data-->'); } @@ -131,7 +137,7 @@ String htmlSerializeEscape(String text, {bool attributeMode = false}) { // TODO(jmesserly): is it faster to build up a list of codepoints? // StringBuffer seems cleaner assuming Dart can unbox 1-char strings. StringBuffer result; - for (int i = 0; i < text.length; i++) { + for (var i = 0; i < text.length; i++) { var ch = text[i]; String replace; switch (ch) { @@ -152,7 +158,7 @@ String htmlSerializeEscape(String text, {bool attributeMode = false}) { break; } if (replace != null) { - if (result == null) result = StringBuffer(text.substring(0, i)); + result ??= StringBuffer(text.substring(0, i)); result.write(replace); } else if (result != null) { result.write(ch); @@ -168,22 +174,22 @@ String htmlSerializeEscape(String text, {bool attributeMode = false}) { /// See also: . bool isVoidElement(String tagName) { switch (tagName) { - case "area": - case "base": - case "br": - case "col": - case "command": - case "embed": - case "hr": - case "img": - case "input": - case "keygen": - case "link": - case "meta": - case "param": - case "source": - case "track": - case "wbr": + case 'area': + case 'base': + case 'br': + case 'col': + case 'command': + case 'embed': + case 'hr': + case 'img': + case 'input': + case 'keygen': + case 'link': + case 'meta': + case 'param': + case 'source': + case 'track': + case 'wbr': return true; } return false; diff --git a/pkgs/html/lib/parser.dart b/pkgs/html/lib/parser.dart index 58ae1c896..c4eafb67f 100644 --- a/pkgs/html/lib/parser.dart +++ b/pkgs/html/lib/parser.dart @@ -56,7 +56,7 @@ Document parse(input, /// additionally pass [sourceUrl] to indicate where the [input] was extracted /// from. DocumentFragment parseFragment(input, - {String container = "div", + {String container = 'div', String encoding, bool generateSpans = false, String sourceUrl}) { @@ -86,7 +86,7 @@ class HtmlParser { // TODO(jmesserly): use enum? /// "quirks" / "limited quirks" / "no quirks" - String compatMode = "no quirks"; + String compatMode = 'no quirks'; /// innerHTML container when parsing document fragment. String innerHTML; @@ -150,7 +150,7 @@ class HtmlParser { this.generateSpans = false, String sourceUrl, TreeBuilder tree}) - : tree = tree != null ? tree : TreeBuilder(true), + : tree = tree ?? TreeBuilder(true), tokenizer = (input is HtmlTokenizer ? input : HtmlTokenizer(input, @@ -203,7 +203,7 @@ class HtmlParser { /// Parse an html5 document fragment into a tree. /// Pass a [container] to change the type of the containing element. /// After parsing, [errors] will be populated with parse errors, if any. - DocumentFragment parseFragment([String container = "div"]) { + DocumentFragment parseFragment([String container = 'div']) { if (container == null) throw ArgumentError('container'); innerHTML = container.toLowerCase(); _parse(); @@ -232,7 +232,7 @@ class HtmlParser { firstStartTag = false; errors.clear(); // "quirks" / "limited quirks" / "no quirks" - compatMode = "no quirks"; + compatMode = 'no quirks'; if (innerHTMLMode) { if (cdataElements.contains(innerHTML)) { @@ -258,11 +258,11 @@ class HtmlParser { } bool isHTMLIntegrationPoint(Element element) { - if (element.localName == "annotation-xml" && + if (element.localName == 'annotation-xml' && element.namespaceUri == Namespaces.mathml) { - var enc = element.attributes["encoding"]; + var enc = element.attributes['encoding']; if (enc != null) enc = asciiUpper2Lower(enc); - return enc == "text/html" || enc == "application/xhtml+xml"; + return enc == 'text/html' || enc == 'application/xhtml+xml'; } else { return htmlIntegrationPointElements .contains(Pair(element.namespaceUri, element.localName)); @@ -282,8 +282,8 @@ class HtmlParser { if (isMathMLTextIntegrationPoint(node)) { if (type == TokenKind.startTag && - (token as StartTagToken).name != "mglyph" && - (token as StartTagToken).name != "malignmark") { + (token as StartTagToken).name != 'mglyph' && + (token as StartTagToken).name != 'malignmark') { return false; } if (type == TokenKind.characters || type == TokenKind.spaceCharacters) { @@ -291,9 +291,9 @@ class HtmlParser { } } - if (node.localName == "annotation-xml" && + if (node.localName == 'annotation-xml' && type == TokenKind.startTag && - (token as StartTagToken).name == "svg") { + (token as StartTagToken).name == 'svg') { return false; } @@ -322,7 +322,7 @@ class HtmlParser { parseError(error.span, error.data, error.messageParams); newToken = null; } else { - Phase localPhase = phase; + var localPhase = phase; if (inForeignContent(token, type)) { localPhase = _inForeignContentPhase; } @@ -352,8 +352,8 @@ class HtmlParser { if (token is StartTagToken) { if (token.selfClosing && !token.selfClosingAcknowledged) { - parseError(token.span, "non-void-element-with-trailing-solidus", - {"name": token.name}); + parseError(token.span, 'non-void-element-with-trailing-solidus', + {'name': token.name}); } } } @@ -390,76 +390,76 @@ class HtmlParser { } void adjustMathMLAttributes(StartTagToken token) { - var orig = token.data.remove("definitionurl"); + var orig = token.data.remove('definitionurl'); if (orig != null) { - token.data["definitionURL"] = orig; + token.data['definitionURL'] = orig; } } void adjustSVGAttributes(StartTagToken token) { final replacements = const { - "attributename": "attributeName", - "attributetype": "attributeType", - "basefrequency": "baseFrequency", - "baseprofile": "baseProfile", - "calcmode": "calcMode", - "clippathunits": "clipPathUnits", - "contentscripttype": "contentScriptType", - "contentstyletype": "contentStyleType", - "diffuseconstant": "diffuseConstant", - "edgemode": "edgeMode", - "externalresourcesrequired": "externalResourcesRequired", - "filterres": "filterRes", - "filterunits": "filterUnits", - "glyphref": "glyphRef", - "gradienttransform": "gradientTransform", - "gradientunits": "gradientUnits", - "kernelmatrix": "kernelMatrix", - "kernelunitlength": "kernelUnitLength", - "keypoints": "keyPoints", - "keysplines": "keySplines", - "keytimes": "keyTimes", - "lengthadjust": "lengthAdjust", - "limitingconeangle": "limitingConeAngle", - "markerheight": "markerHeight", - "markerunits": "markerUnits", - "markerwidth": "markerWidth", - "maskcontentunits": "maskContentUnits", - "maskunits": "maskUnits", - "numoctaves": "numOctaves", - "pathlength": "pathLength", - "patterncontentunits": "patternContentUnits", - "patterntransform": "patternTransform", - "patternunits": "patternUnits", - "pointsatx": "pointsAtX", - "pointsaty": "pointsAtY", - "pointsatz": "pointsAtZ", - "preservealpha": "preserveAlpha", - "preserveaspectratio": "preserveAspectRatio", - "primitiveunits": "primitiveUnits", - "refx": "refX", - "refy": "refY", - "repeatcount": "repeatCount", - "repeatdur": "repeatDur", - "requiredextensions": "requiredExtensions", - "requiredfeatures": "requiredFeatures", - "specularconstant": "specularConstant", - "specularexponent": "specularExponent", - "spreadmethod": "spreadMethod", - "startoffset": "startOffset", - "stddeviation": "stdDeviation", - "stitchtiles": "stitchTiles", - "surfacescale": "surfaceScale", - "systemlanguage": "systemLanguage", - "tablevalues": "tableValues", - "targetx": "targetX", - "targety": "targetY", - "textlength": "textLength", - "viewbox": "viewBox", - "viewtarget": "viewTarget", - "xchannelselector": "xChannelSelector", - "ychannelselector": "yChannelSelector", - "zoomandpan": "zoomAndPan" + 'attributename': 'attributeName', + 'attributetype': 'attributeType', + 'basefrequency': 'baseFrequency', + 'baseprofile': 'baseProfile', + 'calcmode': 'calcMode', + 'clippathunits': 'clipPathUnits', + 'contentscripttype': 'contentScriptType', + 'contentstyletype': 'contentStyleType', + 'diffuseconstant': 'diffuseConstant', + 'edgemode': 'edgeMode', + 'externalresourcesrequired': 'externalResourcesRequired', + 'filterres': 'filterRes', + 'filterunits': 'filterUnits', + 'glyphref': 'glyphRef', + 'gradienttransform': 'gradientTransform', + 'gradientunits': 'gradientUnits', + 'kernelmatrix': 'kernelMatrix', + 'kernelunitlength': 'kernelUnitLength', + 'keypoints': 'keyPoints', + 'keysplines': 'keySplines', + 'keytimes': 'keyTimes', + 'lengthadjust': 'lengthAdjust', + 'limitingconeangle': 'limitingConeAngle', + 'markerheight': 'markerHeight', + 'markerunits': 'markerUnits', + 'markerwidth': 'markerWidth', + 'maskcontentunits': 'maskContentUnits', + 'maskunits': 'maskUnits', + 'numoctaves': 'numOctaves', + 'pathlength': 'pathLength', + 'patterncontentunits': 'patternContentUnits', + 'patterntransform': 'patternTransform', + 'patternunits': 'patternUnits', + 'pointsatx': 'pointsAtX', + 'pointsaty': 'pointsAtY', + 'pointsatz': 'pointsAtZ', + 'preservealpha': 'preserveAlpha', + 'preserveaspectratio': 'preserveAspectRatio', + 'primitiveunits': 'primitiveUnits', + 'refx': 'refX', + 'refy': 'refY', + 'repeatcount': 'repeatCount', + 'repeatdur': 'repeatDur', + 'requiredextensions': 'requiredExtensions', + 'requiredfeatures': 'requiredFeatures', + 'specularconstant': 'specularConstant', + 'specularexponent': 'specularExponent', + 'spreadmethod': 'spreadMethod', + 'startoffset': 'startOffset', + 'stddeviation': 'stdDeviation', + 'stitchtiles': 'stitchTiles', + 'surfacescale': 'surfaceScale', + 'systemlanguage': 'systemLanguage', + 'tablevalues': 'tableValues', + 'targetx': 'targetX', + 'targety': 'targetY', + 'textlength': 'textLength', + 'viewbox': 'viewBox', + 'viewtarget': 'viewTarget', + 'xchannelselector': 'xChannelSelector', + 'ychannelselector': 'yChannelSelector', + 'zoomandpan': 'zoomAndPan' }; for (var originalName in token.data.keys.toList()) { var svgName = replacements[originalName]; @@ -473,18 +473,18 @@ class HtmlParser { // TODO(jmesserly): I don't like mixing non-string objects with strings in // the Node.attributes Map. Is there another solution? final replacements = const { - "xlink:actuate": AttributeName("xlink", "actuate", Namespaces.xlink), - "xlink:arcrole": AttributeName("xlink", "arcrole", Namespaces.xlink), - "xlink:href": AttributeName("xlink", "href", Namespaces.xlink), - "xlink:role": AttributeName("xlink", "role", Namespaces.xlink), - "xlink:show": AttributeName("xlink", "show", Namespaces.xlink), - "xlink:title": AttributeName("xlink", "title", Namespaces.xlink), - "xlink:type": AttributeName("xlink", "type", Namespaces.xlink), - "xml:base": AttributeName("xml", "base", Namespaces.xml), - "xml:lang": AttributeName("xml", "lang", Namespaces.xml), - "xml:space": AttributeName("xml", "space", Namespaces.xml), - "xmlns": AttributeName(null, "xmlns", Namespaces.xmlns), - "xmlns:xlink": AttributeName("xmlns", "xlink", Namespaces.xmlns) + 'xlink:actuate': AttributeName('xlink', 'actuate', Namespaces.xlink), + 'xlink:arcrole': AttributeName('xlink', 'arcrole', Namespaces.xlink), + 'xlink:href': AttributeName('xlink', 'href', Namespaces.xlink), + 'xlink:role': AttributeName('xlink', 'role', Namespaces.xlink), + 'xlink:show': AttributeName('xlink', 'show', Namespaces.xlink), + 'xlink:title': AttributeName('xlink', 'title', Namespaces.xlink), + 'xlink:type': AttributeName('xlink', 'type', Namespaces.xlink), + 'xml:base': AttributeName('xml', 'base', Namespaces.xml), + 'xml:lang': AttributeName('xml', 'lang', Namespaces.xml), + 'xml:space': AttributeName('xml', 'space', Namespaces.xml), + 'xmlns': AttributeName(null, 'xmlns', Namespaces.xmlns), + 'xmlns:xlink': AttributeName('xmlns', 'xlink', Namespaces.xmlns) }; for (var originalName in token.data.keys.toList()) { @@ -500,7 +500,7 @@ class HtmlParser { // specification.) for (var node in tree.openElements.reversed) { var nodeName = node.localName; - bool last = node == tree.openElements[0]; + var last = node == tree.openElements[0]; if (last) { assert(innerHTMLMode); nodeName = innerHTML; @@ -508,10 +508,10 @@ class HtmlParser { // Check for conditions that should only happen in the innerHTML // case switch (nodeName) { - case "select": - case "colgroup": - case "head": - case "html": + case 'select': + case 'colgroup': + case 'head': + case 'html': assert(innerHTMLMode); break; } @@ -519,46 +519,46 @@ class HtmlParser { continue; } switch (nodeName) { - case "select": + case 'select': phase = _inSelectPhase; return; - case "td": + case 'td': phase = _inCellPhase; return; - case "th": + case 'th': phase = _inCellPhase; return; - case "tr": + case 'tr': phase = _inRowPhase; return; - case "tbody": + case 'tbody': phase = _inTableBodyPhase; return; - case "thead": + case 'thead': phase = _inTableBodyPhase; return; - case "tfoot": + case 'tfoot': phase = _inTableBodyPhase; return; - case "caption": + case 'caption': phase = _inCaptionPhase; return; - case "colgroup": + case 'colgroup': phase = _inColumnGroupPhase; return; - case "table": + case 'table': phase = _inTablePhase; return; - case "head": + case 'head': phase = _inBodyPhase; return; - case "body": + case 'body': phase = _inBodyPhase; return; - case "frameset": + case 'frameset': phase = _inFramesetPhase; return; - case "html": + case 'html': phase = _beforeHeadPhase; return; } @@ -569,11 +569,11 @@ class HtmlParser { /// Generic RCDATA/RAWTEXT Parsing algorithm /// [contentType] - RCDATA or RAWTEXT void parseRCDataRawtext(Token token, String contentType) { - assert(contentType == "RAWTEXT" || contentType == "RCDATA"); + assert(contentType == 'RAWTEXT' || contentType == 'RCDATA'); tree.insertElement(token); - if (contentType == "RAWTEXT") { + if (contentType == 'RAWTEXT') { tokenizer.state = tokenizer.rawtextState; } else { tokenizer.state = tokenizer.rcdataState; @@ -615,7 +615,7 @@ class Phase { } Token processDoctype(DoctypeToken token) { - parser.parseError(token.span, "unexpected-doctype"); + parser.parseError(token.span, 'unexpected-doctype'); return null; } @@ -634,8 +634,8 @@ class Phase { } Token startTagHtml(StartTagToken token) { - if (parser.firstStartTag == false && token.name == "html") { - parser.parseError(token.span, "non-html-root"); + if (parser.firstStartTag == false && token.name == 'html') { + parser.parseError(token.span, 'non-html-root'); } // XXX Need a check here to see if the first start tag token emitted is // this token... If it's not, invoke parser.parseError(). @@ -653,7 +653,7 @@ class Phase { /// Helper method for popping openElements. void popOpenElementsUntil(EndTagToken token) { - String name = token.name; + var name = token.name; var node = tree.openElements.removeLast(); while (node.localName != name) { node = tree.openElements.removeLast(); @@ -667,152 +667,157 @@ class Phase { class InitialPhase extends Phase { InitialPhase(parser) : super(parser); + @override Token processSpaceCharacters(SpaceCharactersToken token) { return null; } + @override Token processComment(CommentToken token) { tree.insertComment(token, tree.document); return null; } + @override Token processDoctype(DoctypeToken token) { var name = token.name; - String publicId = token.publicId; + var publicId = token.publicId; var systemId = token.systemId; var correct = token.correct; - if ((name != "html" || + if ((name != 'html' || publicId != null || - systemId != null && systemId != "about:legacy-compat")) { - parser.parseError(token.span, "unknown-doctype"); + systemId != null && systemId != 'about:legacy-compat')) { + parser.parseError(token.span, 'unknown-doctype'); } - if (publicId == null) { - publicId = ""; - } + publicId ??= ''; tree.insertDoctype(token); - if (publicId != "") { + if (publicId != '') { publicId = asciiUpper2Lower(publicId); } if (!correct || - token.name != "html" || + token.name != 'html' || startsWithAny(publicId, const [ - "+//silmaril//dtd html pro v0r11 19970101//", - "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", - "-//as//dtd html 3.0 aswedit + extensions//", - "-//ietf//dtd html 2.0 level 1//", - "-//ietf//dtd html 2.0 level 2//", - "-//ietf//dtd html 2.0 strict level 1//", - "-//ietf//dtd html 2.0 strict level 2//", - "-//ietf//dtd html 2.0 strict//", - "-//ietf//dtd html 2.0//", - "-//ietf//dtd html 2.1e//", - "-//ietf//dtd html 3.0//", - "-//ietf//dtd html 3.2 final//", - "-//ietf//dtd html 3.2//", - "-//ietf//dtd html 3//", - "-//ietf//dtd html level 0//", - "-//ietf//dtd html level 1//", - "-//ietf//dtd html level 2//", - "-//ietf//dtd html level 3//", - "-//ietf//dtd html strict level 0//", - "-//ietf//dtd html strict level 1//", - "-//ietf//dtd html strict level 2//", - "-//ietf//dtd html strict level 3//", - "-//ietf//dtd html strict//", - "-//ietf//dtd html//", - "-//metrius//dtd metrius presentational//", - "-//microsoft//dtd internet explorer 2.0 html strict//", - "-//microsoft//dtd internet explorer 2.0 html//", - "-//microsoft//dtd internet explorer 2.0 tables//", - "-//microsoft//dtd internet explorer 3.0 html strict//", - "-//microsoft//dtd internet explorer 3.0 html//", - "-//microsoft//dtd internet explorer 3.0 tables//", - "-//netscape comm. corp.//dtd html//", - "-//netscape comm. corp.//dtd strict html//", + '+//silmaril//dtd html pro v0r11 19970101//', + '-//advasoft ltd//dtd html 3.0 aswedit + extensions//', + '-//as//dtd html 3.0 aswedit + extensions//', + '-//ietf//dtd html 2.0 level 1//', + '-//ietf//dtd html 2.0 level 2//', + '-//ietf//dtd html 2.0 strict level 1//', + '-//ietf//dtd html 2.0 strict level 2//', + '-//ietf//dtd html 2.0 strict//', + '-//ietf//dtd html 2.0//', + '-//ietf//dtd html 2.1e//', + '-//ietf//dtd html 3.0//', + '-//ietf//dtd html 3.2 final//', + '-//ietf//dtd html 3.2//', + '-//ietf//dtd html 3//', + '-//ietf//dtd html level 0//', + '-//ietf//dtd html level 1//', + '-//ietf//dtd html level 2//', + '-//ietf//dtd html level 3//', + '-//ietf//dtd html strict level 0//', + '-//ietf//dtd html strict level 1//', + '-//ietf//dtd html strict level 2//', + '-//ietf//dtd html strict level 3//', + '-//ietf//dtd html strict//', + '-//ietf//dtd html//', + '-//metrius//dtd metrius presentational//', + '-//microsoft//dtd internet explorer 2.0 html strict//', + '-//microsoft//dtd internet explorer 2.0 html//', + '-//microsoft//dtd internet explorer 2.0 tables//', + '-//microsoft//dtd internet explorer 3.0 html strict//', + '-//microsoft//dtd internet explorer 3.0 html//', + '-//microsoft//dtd internet explorer 3.0 tables//', + '-//netscape comm. corp.//dtd html//', + '-//netscape comm. corp.//dtd strict html//', "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", - "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", - "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", - "-//spyglass//dtd html 2.0 extended//", - "-//sq//dtd html 2.0 hotmetal + extensions//", - "-//sun microsystems corp.//dtd hotjava html//", - "-//sun microsystems corp.//dtd hotjava strict html//", - "-//w3c//dtd html 3 1995-03-24//", - "-//w3c//dtd html 3.2 draft//", - "-//w3c//dtd html 3.2 final//", - "-//w3c//dtd html 3.2//", - "-//w3c//dtd html 3.2s draft//", - "-//w3c//dtd html 4.0 frameset//", - "-//w3c//dtd html 4.0 transitional//", - "-//w3c//dtd html experimental 19960712//", - "-//w3c//dtd html experimental 970421//", - "-//w3c//dtd w3 html//", - "-//w3o//dtd w3 html 3.0//", - "-//webtechs//dtd mozilla html 2.0//", - "-//webtechs//dtd mozilla html//" + '-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//', + '-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//', + '-//spyglass//dtd html 2.0 extended//', + '-//sq//dtd html 2.0 hotmetal + extensions//', + '-//sun microsystems corp.//dtd hotjava html//', + '-//sun microsystems corp.//dtd hotjava strict html//', + '-//w3c//dtd html 3 1995-03-24//', + '-//w3c//dtd html 3.2 draft//', + '-//w3c//dtd html 3.2 final//', + '-//w3c//dtd html 3.2//', + '-//w3c//dtd html 3.2s draft//', + '-//w3c//dtd html 4.0 frameset//', + '-//w3c//dtd html 4.0 transitional//', + '-//w3c//dtd html experimental 19960712//', + '-//w3c//dtd html experimental 970421//', + '-//w3c//dtd w3 html//', + '-//w3o//dtd w3 html 3.0//', + '-//webtechs//dtd mozilla html 2.0//', + '-//webtechs//dtd mozilla html//' ]) || const [ - "-//w3o//dtd w3 html strict 3.0//en//", - "-/w3c/dtd html 4.0 transitional/en", - "html" + '-//w3o//dtd w3 html strict 3.0//en//', + '-/w3c/dtd html 4.0 transitional/en', + 'html' ].contains(publicId) || startsWithAny(publicId, const [ - "-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//" + '-//w3c//dtd html 4.01 frameset//', + '-//w3c//dtd html 4.01 transitional//' ]) && systemId == null || systemId != null && systemId.toLowerCase() == - "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") { - parser.compatMode = "quirks"; + 'http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd') { + parser.compatMode = 'quirks'; } else if (startsWithAny(publicId, const [ - "-//w3c//dtd xhtml 1.0 frameset//", - "-//w3c//dtd xhtml 1.0 transitional//" + '-//w3c//dtd xhtml 1.0 frameset//', + '-//w3c//dtd xhtml 1.0 transitional//' ]) || startsWithAny(publicId, const [ - "-//w3c//dtd html 4.01 frameset//", - "-//w3c//dtd html 4.01 transitional//" + '-//w3c//dtd html 4.01 frameset//', + '-//w3c//dtd html 4.01 transitional//' ]) && systemId != null) { - parser.compatMode = "limited quirks"; + parser.compatMode = 'limited quirks'; } parser.phase = parser._beforeHtmlPhase; return null; } void anythingElse() { - parser.compatMode = "quirks"; + parser.compatMode = 'quirks'; parser.phase = parser._beforeHtmlPhase; } + @override Token processCharacters(CharactersToken token) { - parser.parseError(token.span, "expected-doctype-but-got-chars"); + parser.parseError(token.span, 'expected-doctype-but-got-chars'); anythingElse(); return token; } + @override Token processStartTag(StartTagToken token) { parser.parseError( - token.span, "expected-doctype-but-got-start-tag", {"name": token.name}); + token.span, 'expected-doctype-but-got-start-tag', {'name': token.name}); anythingElse(); return token; } + @override Token processEndTag(EndTagToken token) { parser.parseError( - token.span, "expected-doctype-but-got-end-tag", {"name": token.name}); + token.span, 'expected-doctype-but-got-end-tag', {'name': token.name}); anythingElse(); return token; } + @override bool processEOF() { - parser.parseError(parser._lastSpan, "expected-doctype-but-got-eof"); + parser.parseError(parser._lastSpan, 'expected-doctype-but-got-eof'); anythingElse(); return true; } @@ -824,49 +829,56 @@ class BeforeHtmlPhase extends Phase { // helper methods void insertHtmlElement() { tree.insertRoot( - StartTagToken("html", data: LinkedHashMap())); + StartTagToken('html', data: LinkedHashMap())); parser.phase = parser._beforeHeadPhase; } // other + @override bool processEOF() { insertHtmlElement(); return true; } + @override Token processComment(CommentToken token) { tree.insertComment(token, tree.document); return null; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { return null; } + @override Token processCharacters(CharactersToken token) { insertHtmlElement(); return token; } + @override + @override Token processStartTag(StartTagToken token) { - if (token.name == "html") { + if (token.name == 'html') { parser.firstStartTag = true; } insertHtmlElement(); return token; } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "head": - case "body": - case "html": - case "br": + case 'head': + case 'body': + case 'html': + case 'br': insertHtmlElement(); return token; default: parser.parseError( - token.span, "unexpected-end-tag-before-html", {"name": token.name}); + token.span, 'unexpected-end-tag-before-html', {'name': token.name}); return null; } } @@ -875,6 +887,7 @@ class BeforeHtmlPhase extends Phase { class BeforeHeadPhase extends Phase { BeforeHeadPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { case 'html': @@ -887,12 +900,13 @@ class BeforeHeadPhase extends Phase { } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "head": - case "body": - case "html": - case "br": + case 'head': + case 'body': + case 'html': + case 'br': return endTagImplyHead(token); default: endTagOther(token); @@ -900,20 +914,24 @@ class BeforeHeadPhase extends Phase { } } + @override bool processEOF() { - startTagHead(StartTagToken("head", data: LinkedHashMap())); + startTagHead(StartTagToken('head', data: LinkedHashMap())); return true; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { return null; } + @override Token processCharacters(CharactersToken token) { - startTagHead(StartTagToken("head", data: LinkedHashMap())); + startTagHead(StartTagToken('head', data: LinkedHashMap())); return token; } + @override Token startTagHtml(StartTagToken token) { return parser._inBodyPhase.processStartTag(token); } @@ -925,50 +943,51 @@ class BeforeHeadPhase extends Phase { } Token startTagOther(StartTagToken token) { - startTagHead(StartTagToken("head", data: LinkedHashMap())); + startTagHead(StartTagToken('head', data: LinkedHashMap())); return token; } Token endTagImplyHead(EndTagToken token) { - startTagHead(StartTagToken("head", data: LinkedHashMap())); + startTagHead(StartTagToken('head', data: LinkedHashMap())); return token; } void endTagOther(EndTagToken token) { parser.parseError( - token.span, "end-tag-after-implied-root", {"name": token.name}); + token.span, 'end-tag-after-implied-root', {'name': token.name}); } } class InHeadPhase extends Phase { InHeadPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "title": + case 'title': startTagTitle(token); return null; - case "noscript": - case "noframes": - case "style": + case 'noscript': + case 'noframes': + case 'style': startTagNoScriptNoFramesStyle(token); return null; - case "script": + case 'script': startTagScript(token); return null; - case "base": - case "basefont": - case "bgsound": - case "command": - case "link": + case 'base': + case 'basefont': + case 'bgsound': + case 'command': + case 'link': startTagBaseLinkCommand(token); return null; - case "meta": + case 'meta': startTagMeta(token); return null; - case "head": + case 'head': startTagHead(token); return null; default: @@ -976,14 +995,15 @@ class InHeadPhase extends Phase { } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "head": + case 'head': endTagHead(token); return null; - case "br": - case "html": - case "body": + case 'br': + case 'html': + case 'body': return endTagHtmlBodyBr(token); default: endTagOther(token); @@ -992,22 +1012,25 @@ class InHeadPhase extends Phase { } // the real thing + @override bool processEOF() { anythingElse(); return true; } + @override Token processCharacters(CharactersToken token) { anythingElse(); return token; } + @override Token startTagHtml(StartTagToken token) { return parser._inBodyPhase.processStartTag(token); } void startTagHead(StartTagToken token) { - parser.parseError(token.span, "two-heads-are-not-better-than-one"); + parser.parseError(token.span, 'two-heads-are-not-better-than-one'); } void startTagBaseLinkCommand(StartTagToken token) { @@ -1023,8 +1046,8 @@ class InHeadPhase extends Phase { var attributes = token.data; if (!parser.tokenizer.stream.charEncodingCertain) { - var charset = attributes["charset"]; - var content = attributes["content"]; + var charset = attributes['charset']; + var content = attributes['content']; if (charset != null) { parser.tokenizer.stream.changeEncoding(charset); } else if (content != null) { @@ -1036,12 +1059,12 @@ class InHeadPhase extends Phase { } void startTagTitle(StartTagToken token) { - parser.parseRCDataRawtext(token, "RCDATA"); + parser.parseRCDataRawtext(token, 'RCDATA'); } void startTagNoScriptNoFramesStyle(StartTagToken token) { // Need to decide whether to implement the scripting-disabled case - parser.parseRCDataRawtext(token, "RAWTEXT"); + parser.parseRCDataRawtext(token, 'RAWTEXT'); } void startTagScript(StartTagToken token) { @@ -1058,7 +1081,7 @@ class InHeadPhase extends Phase { void endTagHead(EndTagToken token) { var node = parser.tree.openElements.removeLast(); - assert(node.localName == "head"); + assert(node.localName == 'head'); node.endSourceSpan = token.span; parser.phase = parser._afterHeadPhase; } @@ -1069,11 +1092,11 @@ class InHeadPhase extends Phase { } void endTagOther(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } void anythingElse() { - endTagHead(EndTagToken("head")); + endTagHead(EndTagToken('head')); } } @@ -1085,28 +1108,29 @@ class InHeadPhase extends Phase { class AfterHeadPhase extends Phase { AfterHeadPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "body": + case 'body': startTagBody(token); return null; - case "frameset": + case 'frameset': startTagFrameset(token); return null; - case "base": - case "basefont": - case "bgsound": - case "link": - case "meta": - case "noframes": - case "script": - case "style": - case "title": + case 'base': + case 'basefont': + case 'bgsound': + case 'link': + case 'meta': + case 'noframes': + case 'script': + case 'style': + case 'title': startTagFromHead(token); return null; - case "head": + case 'head': startTagHead(token); return null; default: @@ -1114,11 +1138,12 @@ class AfterHeadPhase extends Phase { } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "body": - case "html": - case "br": + case 'body': + case 'html': + case 'br': return endTagHtmlBodyBr(token); default: endTagOther(token); @@ -1126,16 +1151,19 @@ class AfterHeadPhase extends Phase { } } + @override bool processEOF() { anythingElse(); return true; } + @override Token processCharacters(CharactersToken token) { anythingElse(); return token; } + @override Token startTagHtml(StartTagToken token) { return parser._inBodyPhase.processStartTag(token); } @@ -1152,12 +1180,12 @@ class AfterHeadPhase extends Phase { } void startTagFromHead(StartTagToken token) { - parser.parseError(token.span, "unexpected-start-tag-out-of-my-head", - {"name": token.name}); + parser.parseError(token.span, 'unexpected-start-tag-out-of-my-head', + {'name': token.name}); tree.openElements.add(tree.headPointer); parser._inHeadPhase.processStartTag(token); for (var node in tree.openElements.reversed) { - if (node.localName == "head") { + if (node.localName == 'head') { tree.openElements.remove(node); break; } @@ -1165,7 +1193,7 @@ class AfterHeadPhase extends Phase { } void startTagHead(StartTagToken token) { - parser.parseError(token.span, "unexpected-start-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-start-tag', {'name': token.name}); } Token startTagOther(StartTagToken token) { @@ -1179,12 +1207,12 @@ class AfterHeadPhase extends Phase { } void endTagOther(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } void anythingElse() { tree.insertElement( - StartTagToken("body", data: LinkedHashMap())); + StartTagToken('body', data: LinkedHashMap())); parser.phase = parser._inBodyPhase; parser.framesetOK = true; } @@ -1199,171 +1227,172 @@ class InBodyPhase extends Phase { // the really-really-really-very crazy mode InBodyPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "base": - case "basefont": - case "bgsound": - case "command": - case "link": - case "meta": - case "noframes": - case "script": - case "style": - case "title": + case 'base': + case 'basefont': + case 'bgsound': + case 'command': + case 'link': + case 'meta': + case 'noframes': + case 'script': + case 'style': + case 'title': return startTagProcessInHead(token); - case "body": + case 'body': startTagBody(token); return null; - case "frameset": + case 'frameset': startTagFrameset(token); return null; - case "address": - case "article": - case "aside": - case "blockquote": - case "center": - case "details": - case "dir": - case "div": - case "dl": - case "fieldset": - case "figcaption": - case "figure": - case "footer": - case "header": - case "hgroup": - case "menu": - case "nav": - case "ol": - case "p": - case "section": - case "summary": - case "ul": + case 'address': + case 'article': + case 'aside': + case 'blockquote': + case 'center': + case 'details': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'figcaption': + case 'figure': + case 'footer': + case 'header': + case 'hgroup': + case 'menu': + case 'nav': + case 'ol': + case 'p': + case 'section': + case 'summary': + case 'ul': startTagCloseP(token); return null; // headingElements - case "h1": - case "h2": - case "h3": - case "h4": - case "h5": - case "h6": + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': startTagHeading(token); return null; - case "pre": - case "listing": + case 'pre': + case 'listing': startTagPreListing(token); return null; - case "form": + case 'form': startTagForm(token); return null; - case "li": - case "dd": - case "dt": + case 'li': + case 'dd': + case 'dt': startTagListItem(token); return null; - case "plaintext": + case 'plaintext': startTagPlaintext(token); return null; - case "a": + case 'a': startTagA(token); return null; - case "b": - case "big": - case "code": - case "em": - case "font": - case "i": - case "s": - case "small": - case "strike": - case "strong": - case "tt": - case "u": + case 'b': + case 'big': + case 'code': + case 'em': + case 'font': + case 'i': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': startTagFormatting(token); return null; - case "nobr": + case 'nobr': startTagNobr(token); return null; - case "button": + case 'button': return startTagButton(token); - case "applet": - case "marquee": - case "object": + case 'applet': + case 'marquee': + case 'object': startTagAppletMarqueeObject(token); return null; - case "xmp": + case 'xmp': startTagXmp(token); return null; - case "table": + case 'table': startTagTable(token); return null; - case "area": - case "br": - case "embed": - case "img": - case "keygen": - case "wbr": + case 'area': + case 'br': + case 'embed': + case 'img': + case 'keygen': + case 'wbr': startTagVoidFormatting(token); return null; - case "param": - case "source": - case "track": + case 'param': + case 'source': + case 'track': startTagParamSource(token); return null; - case "input": + case 'input': startTagInput(token); return null; - case "hr": + case 'hr': startTagHr(token); return null; - case "image": + case 'image': startTagImage(token); return null; - case "isindex": + case 'isindex': startTagIsIndex(token); return null; - case "textarea": + case 'textarea': startTagTextarea(token); return null; - case "iframe": + case 'iframe': startTagIFrame(token); return null; - case "noembed": - case "noscript": + case 'noembed': + case 'noscript': startTagRawtext(token); return null; - case "select": + case 'select': startTagSelect(token); return null; - case "rp": - case "rt": + case 'rp': + case 'rt': startTagRpRt(token); return null; - case "option": - case "optgroup": + case 'option': + case 'optgroup': startTagOpt(token); return null; - case "math": + case 'math': startTagMath(token); return null; - case "svg": + case 'svg': startTagSvg(token); return null; - case "caption": - case "col": - case "colgroup": - case "frame": - case "head": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": + case 'caption': + case 'col': + case 'colgroup': + case 'frame': + case 'head': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': startTagMisplaced(token); return null; default: @@ -1371,80 +1400,81 @@ class InBodyPhase extends Phase { } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "body": + case 'body': endTagBody(token); return null; - case "html": + case 'html': return endTagHtml(token); - case "address": - case "article": - case "aside": - case "blockquote": - case "center": - case "details": - case "dir": - case "div": - case "dl": - case "fieldset": - case "figcaption": - case "figure": - case "footer": - case "header": - case "hgroup": - case "listing": - case "menu": - case "nav": - case "ol": - case "pre": - case "section": - case "summary": - case "ul": + case 'address': + case 'article': + case 'aside': + case 'blockquote': + case 'center': + case 'details': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'figcaption': + case 'figure': + case 'footer': + case 'header': + case 'hgroup': + case 'listing': + case 'menu': + case 'nav': + case 'ol': + case 'pre': + case 'section': + case 'summary': + case 'ul': endTagBlock(token); return null; - case "form": + case 'form': endTagForm(token); return null; - case "p": + case 'p': endTagP(token); return null; - case "dd": - case "dt": - case "li": + case 'dd': + case 'dt': + case 'li': endTagListItem(token); return null; // headingElements - case "h1": - case "h2": - case "h3": - case "h4": - case "h5": - case "h6": + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': endTagHeading(token); return null; - case "a": - case "b": - case "big": - case "code": - case "em": - case "font": - case "i": - case "nobr": - case "s": - case "small": - case "strike": - case "strong": - case "tt": - case "u": + case 'a': + case 'b': + case 'big': + case 'code': + case 'em': + case 'font': + case 'i': + case 'nobr': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': endTagFormatting(token); return null; - case "applet": - case "marquee": - case "object": + case 'applet': + case 'marquee': + case 'object': endTagAppletMarqueeObject(token); return null; - case "br": + case 'br': endTagBr(token); return null; default: @@ -1491,24 +1521,25 @@ class InBodyPhase extends Phase { } // the real deal + @override bool processEOF() { for (var node in tree.openElements.reversed) { switch (node.localName) { - case "dd": - case "dt": - case "li": - case "p": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": - case "body": - case "html": + case 'dd': + case 'dt': + case 'li': + case 'p': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + case 'body': + case 'html': continue; } - parser.parseError(node.sourceSpan, "expected-closing-tag-but-got-eof"); + parser.parseError(node.sourceSpan, 'expected-closing-tag-but-got-eof'); break; } //Stop parsing @@ -1520,9 +1551,9 @@ class InBodyPhase extends Phase { // want to drop leading newlines var data = token.data; dropNewline = false; - if (data.startsWith("\n")) { + if (data.startsWith('\n')) { var lastOpen = tree.openElements.last; - if (const ["pre", "listing", "textarea"].contains(lastOpen.localName) && + if (const ['pre', 'listing', 'textarea'].contains(lastOpen.localName) && !lastOpen.hasContent()) { data = data.substring(1); } @@ -1533,8 +1564,9 @@ class InBodyPhase extends Phase { } } + @override Token processCharacters(CharactersToken token) { - if (token.data == "\u0000") { + if (token.data == '\u0000') { //The tokenizer should always emit null on its own return null; } @@ -1546,6 +1578,7 @@ class InBodyPhase extends Phase { return null; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { if (dropNewline) { processSpaceCharactersDropNewline(token); @@ -1561,9 +1594,9 @@ class InBodyPhase extends Phase { } void startTagBody(StartTagToken token) { - parser.parseError(token.span, "unexpected-start-tag", {"name": "body"}); + parser.parseError(token.span, 'unexpected-start-tag', {'name': 'body'}); if (tree.openElements.length == 1 || - tree.openElements[1].localName != "body") { + tree.openElements[1].localName != 'body') { assert(parser.innerHTMLMode); } else { parser.framesetOK = false; @@ -1574,15 +1607,15 @@ class InBodyPhase extends Phase { } void startTagFrameset(StartTagToken token) { - parser.parseError(token.span, "unexpected-start-tag", {"name": "frameset"}); + parser.parseError(token.span, 'unexpected-start-tag', {'name': 'frameset'}); if ((tree.openElements.length == 1 || - tree.openElements[1].localName != "body")) { + tree.openElements[1].localName != 'body')) { assert(parser.innerHTMLMode); } else if (parser.framesetOK) { if (tree.openElements[1].parentNode != null) { tree.openElements[1].parentNode.nodes.remove(tree.openElements[1]); } - while (tree.openElements.last.localName != "html") { + while (tree.openElements.last.localName != 'html') { tree.openElements.removeLast(); } tree.insertElement(token); @@ -1591,15 +1624,15 @@ class InBodyPhase extends Phase { } void startTagCloseP(StartTagToken token) { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } tree.insertElement(token); } void startTagPreListing(StartTagToken token) { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } tree.insertElement(token); parser.framesetOK = false; @@ -1608,10 +1641,10 @@ class InBodyPhase extends Phase { void startTagForm(StartTagToken token) { if (tree.formPointer != null) { - parser.parseError(token.span, "unexpected-start-tag", {"name": "form"}); + parser.parseError(token.span, 'unexpected-start-tag', {'name': 'form'}); } else { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } tree.insertElement(token); tree.formPointer = tree.openElements.last; @@ -1622,9 +1655,9 @@ class InBodyPhase extends Phase { parser.framesetOK = false; final stopNamesMap = const { - "li": ["li"], - "dt": ["dt", "dd"], - "dd": ["dt", "dd"] + 'li': ['li'], + 'dt': ['dt', 'dd'], + 'dd': ['dt', 'dd'] }; var stopNames = stopNamesMap[token.name]; for (var node in tree.openElements.reversed) { @@ -1633,44 +1666,44 @@ class InBodyPhase extends Phase { break; } if (specialElements.contains(getElementNameTuple(node)) && - !const ["address", "div", "p"].contains(node.localName)) { + !const ['address', 'div', 'p'].contains(node.localName)) { break; } } - if (tree.elementInScope("p", variant: "button")) { - parser.phase.processEndTag(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + parser.phase.processEndTag(EndTagToken('p')); } tree.insertElement(token); } void startTagPlaintext(StartTagToken token) { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } tree.insertElement(token); parser.tokenizer.state = parser.tokenizer.plaintextState; } void startTagHeading(StartTagToken token) { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } if (headingElements.contains(tree.openElements.last.localName)) { parser - .parseError(token.span, "unexpected-start-tag", {"name": token.name}); + .parseError(token.span, 'unexpected-start-tag', {'name': token.name}); tree.openElements.removeLast(); } tree.insertElement(token); } void startTagA(StartTagToken token) { - var afeAElement = tree.elementInActiveFormattingElements("a"); + var afeAElement = tree.elementInActiveFormattingElements('a'); if (afeAElement != null) { - parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", - {"startName": "a", "endName": "a"}); - endTagFormatting(EndTagToken("a")); + parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag', + {'startName': 'a', 'endName': 'a'}); + endTagFormatting(EndTagToken('a')); tree.openElements.remove(afeAElement); tree.activeFormattingElements.remove(afeAElement); } @@ -1685,10 +1718,10 @@ class InBodyPhase extends Phase { void startTagNobr(StartTagToken token) { tree.reconstructActiveFormattingElements(); - if (tree.elementInScope("nobr")) { - parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", - {"startName": "nobr", "endName": "nobr"}); - processEndTag(EndTagToken("nobr")); + if (tree.elementInScope('nobr')) { + parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag', + {'startName': 'nobr', 'endName': 'nobr'}); + processEndTag(EndTagToken('nobr')); // XXX Need tests that trigger the following tree.reconstructActiveFormattingElements(); } @@ -1696,10 +1729,10 @@ class InBodyPhase extends Phase { } Token startTagButton(StartTagToken token) { - if (tree.elementInScope("button")) { - parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", - {"startName": "button", "endName": "button"}); - processEndTag(EndTagToken("button")); + if (tree.elementInScope('button')) { + parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag', + {'startName': 'button', 'endName': 'button'}); + processEndTag(EndTagToken('button')); return token; } else { tree.reconstructActiveFormattingElements(); @@ -1717,18 +1750,18 @@ class InBodyPhase extends Phase { } void startTagXmp(StartTagToken token) { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } tree.reconstructActiveFormattingElements(); parser.framesetOK = false; - parser.parseRCDataRawtext(token, "RAWTEXT"); + parser.parseRCDataRawtext(token, 'RAWTEXT'); } void startTagTable(StartTagToken token) { - if (parser.compatMode != "quirks") { - if (tree.elementInScope("p", variant: "button")) { - processEndTag(EndTagToken("p")); + if (parser.compatMode != 'quirks') { + if (tree.elementInScope('p', variant: 'button')) { + processEndTag(EndTagToken('p')); } } tree.insertElement(token); @@ -1747,7 +1780,7 @@ class InBodyPhase extends Phase { void startTagInput(StartTagToken token) { var savedFramesetOK = parser.framesetOK; startTagVoidFormatting(token); - if (asciiUpper2Lower(token.data["type"]) == "hidden") { + if (asciiUpper2Lower(token.data['type']) == 'hidden') { //input type=hidden doesn't change framesetOK parser.framesetOK = savedFramesetOK; } @@ -1760,8 +1793,8 @@ class InBodyPhase extends Phase { } void startTagHr(StartTagToken token) { - if (tree.elementInScope("p", variant: "button")) { - endTagP(EndTagToken("p")); + if (tree.elementInScope('p', variant: 'button')) { + endTagP(EndTagToken('p')); } tree.insertElement(token); tree.openElements.removeLast(); @@ -1771,43 +1804,41 @@ class InBodyPhase extends Phase { void startTagImage(StartTagToken token) { // No really... - parser.parseError(token.span, "unexpected-start-tag-treated-as", - {"originalName": "image", "newName": "img"}); + parser.parseError(token.span, 'unexpected-start-tag-treated-as', + {'originalName': 'image', 'newName': 'img'}); processStartTag( - StartTagToken("img", data: token.data, selfClosing: token.selfClosing)); + StartTagToken('img', data: token.data, selfClosing: token.selfClosing)); } void startTagIsIndex(StartTagToken token) { - parser.parseError(token.span, "deprecated-tag", {"name": "isindex"}); + parser.parseError(token.span, 'deprecated-tag', {'name': 'isindex'}); if (tree.formPointer != null) { return; } - var formAttrs = LinkedHashMap(); - var dataAction = token.data["action"]; + var formAttrs = {}; + var dataAction = token.data['action']; if (dataAction != null) { - formAttrs["action"] = dataAction; + formAttrs['action'] = dataAction; } - processStartTag(StartTagToken("form", data: formAttrs)); + processStartTag(StartTagToken('form', data: formAttrs)); processStartTag( - StartTagToken("hr", data: LinkedHashMap())); + StartTagToken('hr', data: LinkedHashMap())); processStartTag( - StartTagToken("label", data: LinkedHashMap())); + StartTagToken('label', data: LinkedHashMap())); // XXX Localization ... - var prompt = token.data["prompt"]; - if (prompt == null) { - prompt = "This is a searchable index. Enter search keywords: "; - } + var prompt = token.data['prompt']; + prompt ??= 'This is a searchable index. Enter search keywords: '; processCharacters(CharactersToken(prompt)); var attributes = LinkedHashMap.from(token.data); attributes.remove('action'); attributes.remove('prompt'); - attributes["name"] = "isindex"; - processStartTag(StartTagToken("input", + attributes['name'] = 'isindex'; + processStartTag(StartTagToken('input', data: attributes, selfClosing: token.selfClosing)); - processEndTag(EndTagToken("label")); + processEndTag(EndTagToken('label')); processStartTag( - StartTagToken("hr", data: LinkedHashMap())); - processEndTag(EndTagToken("form")); + StartTagToken('hr', data: LinkedHashMap())); + processEndTag(EndTagToken('form')); } void startTagTextarea(StartTagToken token) { @@ -1824,12 +1855,12 @@ class InBodyPhase extends Phase { /// iframe, noembed noframes, noscript(if scripting enabled). void startTagRawtext(StartTagToken token) { - parser.parseRCDataRawtext(token, "RAWTEXT"); + parser.parseRCDataRawtext(token, 'RAWTEXT'); } void startTagOpt(StartTagToken token) { - if (tree.openElements.last.localName == "option") { - parser.phase.processEndTag(EndTagToken("option")); + if (tree.openElements.last.localName == 'option') { + parser.phase.processEndTag(EndTagToken('option')); } tree.reconstructActiveFormattingElements(); parser.tree.insertElement(token); @@ -1853,10 +1884,10 @@ class InBodyPhase extends Phase { } void startTagRpRt(StartTagToken token) { - if (tree.elementInScope("ruby")) { + if (tree.elementInScope('ruby')) { tree.generateImpliedEndTags(); var last = tree.openElements.last; - if (last.localName != "ruby") { + if (last.localName != 'ruby') { parser.parseError(last.sourceSpan, 'undefined-error'); } } @@ -1898,7 +1929,7 @@ class InBodyPhase extends Phase { /// "tr", "noscript" void startTagMisplaced(StartTagToken token) { parser.parseError( - token.span, "unexpected-start-tag-ignored", {"name": token.name}); + token.span, 'unexpected-start-tag-ignored', {'name': token.name}); } Token startTagOther(StartTagToken token) { @@ -1908,50 +1939,50 @@ class InBodyPhase extends Phase { } void endTagP(EndTagToken token) { - if (!tree.elementInScope("p", variant: "button")) { + if (!tree.elementInScope('p', variant: 'button')) { startTagCloseP( - StartTagToken("p", data: LinkedHashMap())); - parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); - endTagP(EndTagToken("p")); + StartTagToken('p', data: LinkedHashMap())); + parser.parseError(token.span, 'unexpected-end-tag', {'name': 'p'}); + endTagP(EndTagToken('p')); } else { - tree.generateImpliedEndTags("p"); - if (tree.openElements.last.localName != "p") { - parser.parseError(token.span, "unexpected-end-tag", {"name": "p"}); + tree.generateImpliedEndTags('p'); + if (tree.openElements.last.localName != 'p') { + parser.parseError(token.span, 'unexpected-end-tag', {'name': 'p'}); } popOpenElementsUntil(token); } } void endTagBody(EndTagToken token) { - if (!tree.elementInScope("body")) { + 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)) { + for (var node in slice(tree.openElements, 2)) { switch (node.localName) { - case "dd": - case "dt": - case "li": - case "optgroup": - case "option": - case "p": - case "rp": - case "rt": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": - case "body": - case "html": + case 'dd': + case 'dt': + case 'li': + case 'optgroup': + case 'option': + case 'p': + case 'rp': + case 'rt': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + case 'body': + case 'html': continue; } // Not sure this is the correct name for the parse error - parser.parseError(token.span, "expected-one-end-tag-but-got-another", - {"gotName": "body", "expectedName": node.localName}); + parser.parseError(token.span, 'expected-one-end-tag-but-got-another', + {'gotName': 'body', 'expectedName': node.localName}); break; } } @@ -1960,8 +1991,8 @@ class InBodyPhase extends Phase { Token endTagHtml(EndTagToken token) { //We repeat the test for the body end tag token being ignored here - if (tree.elementInScope("body")) { - endTagBody(EndTagToken("body")); + if (tree.elementInScope('body')) { + endTagBody(EndTagToken('body')); return token; } return null; @@ -1969,7 +2000,7 @@ class InBodyPhase extends Phase { void endTagBlock(EndTagToken token) { //Put us back in the right whitespace handling mode - if (token.name == "pre") { + if (token.name == 'pre') { dropNewline = false; } var inScope = tree.elementInScope(token.name); @@ -1977,7 +2008,7 @@ class InBodyPhase extends Phase { tree.generateImpliedEndTags(); } if (tree.openElements.last.localName != token.name) { - parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); + parser.parseError(token.span, 'end-tag-too-early', {'name': token.name}); } if (inScope) { popOpenElementsUntil(token); @@ -1988,12 +2019,12 @@ class InBodyPhase extends Phase { var node = tree.formPointer; tree.formPointer = null; if (node == null || !tree.elementInScope(node)) { - parser.parseError(token.span, "unexpected-end-tag", {"name": "form"}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': 'form'}); } else { tree.generateImpliedEndTags(); if (tree.openElements.last != node) { parser.parseError( - token.span, "end-tag-too-early-ignored", {"name": "form"}); + token.span, 'end-tag-too-early-ignored', {'name': 'form'}); } tree.openElements.remove(node); node.endSourceSpan = token.span; @@ -2002,18 +2033,18 @@ class InBodyPhase extends Phase { void endTagListItem(EndTagToken token) { String variant; - if (token.name == "li") { - variant = "list"; + if (token.name == 'li') { + variant = 'list'; } else { variant = null; } if (!tree.elementInScope(token.name, variant: variant)) { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } else { tree.generateImpliedEndTags(token.name); if (tree.openElements.last.localName != token.name) { parser - .parseError(token.span, "end-tag-too-early", {"name": token.name}); + .parseError(token.span, 'end-tag-too-early', {'name': token.name}); } popOpenElementsUntil(token); } @@ -2027,12 +2058,12 @@ class InBodyPhase extends Phase { } } if (tree.openElements.last.localName != token.name) { - parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); + parser.parseError(token.span, 'end-tag-too-early', {'name': token.name}); } for (var item in headingElements) { if (tree.elementInScope(item)) { - Element node = tree.openElements.removeLast(); + var node = tree.openElements.removeLast(); while (!headingElements.contains(node.localName)) { node = tree.openElements.removeLast(); } @@ -2051,7 +2082,7 @@ class InBodyPhase extends Phase { // updated spec. This needs a pass over it to verify that it still matches. // In particular the html5lib Python code skiped "step 4", I'm not sure why. // XXX Better parseError messages appreciated. - int outerLoopCounter = 0; + var outerLoopCounter = 0; while (outerLoopCounter < 8) { outerLoopCounter += 1; @@ -2062,12 +2093,12 @@ class InBodyPhase extends Phase { (tree.openElements.contains(formattingElement) && !tree.elementInScope(formattingElement.localName))) { parser.parseError( - token.span, "adoption-agency-1.1", {"name": token.name}); + token.span, 'adoption-agency-1.1', {'name': token.name}); return; // Step 1 paragraph 2 } else if (!tree.openElements.contains(formattingElement)) { parser.parseError( - token.span, "adoption-agency-1.2", {"name": token.name}); + token.span, 'adoption-agency-1.2', {'name': token.name}); tree.activeFormattingElements.remove(formattingElement); return; } @@ -2075,14 +2106,14 @@ class InBodyPhase extends Phase { // Step 1 paragraph 3 if (formattingElement != tree.openElements.last) { parser.parseError( - token.span, "adoption-agency-1.3", {"name": token.name}); + token.span, 'adoption-agency-1.3', {'name': token.name}); } // Step 2 // Start of the adoption agency algorithm proper var afeIndex = tree.openElements.indexOf(formattingElement); Node furthestBlock; - for (Node element in slice(tree.openElements, afeIndex)) { + for (var element in slice(tree.openElements, afeIndex)) { if (specialElements.contains(getElementNameTuple(element))) { furthestBlock = element; break; @@ -2090,7 +2121,7 @@ class InBodyPhase extends Phase { } // Step 3 if (furthestBlock == null) { - Element element = tree.openElements.removeLast(); + var element = tree.openElements.removeLast(); while (element != formattingElement) { element = tree.openElements.removeLast(); } @@ -2111,9 +2142,9 @@ class InBodyPhase extends Phase { var bookmark = tree.activeFormattingElements.indexOf(formattingElement); // Step 6 - Node lastNode = furthestBlock; + var lastNode = furthestBlock; var node = furthestBlock; - int innerLoopCounter = 0; + var innerLoopCounter = 0; var index = tree.openElements.indexOf(node); while (innerLoopCounter < 3) { @@ -2162,7 +2193,7 @@ class InBodyPhase extends Phase { lastNode.parentNode.nodes.remove(lastNode); } - if (const ["table", "tbody", "tfoot", "thead", "tr"] + if (const ['table', 'tbody', 'tfoot', 'thead', 'tr'] .contains(commonAncestor.localName)) { var nodePos = tree.getTableMisnestedNodePosition(); nodePos[0].insertBefore(lastNode, nodePos[1]); @@ -2196,7 +2227,7 @@ class InBodyPhase extends Phase { tree.generateImpliedEndTags(); } if (tree.openElements.last.localName != token.name) { - parser.parseError(token.span, "end-tag-too-early", {"name": token.name}); + parser.parseError(token.span, 'end-tag-too-early', {'name': token.name}); } if (tree.elementInScope(token.name)) { popOpenElementsUntil(token); @@ -2205,11 +2236,11 @@ class InBodyPhase extends Phase { } void endTagBr(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag-treated-as", - {"originalName": "br", "newName": "br element"}); + parser.parseError(token.span, 'unexpected-end-tag-treated-as', + {'originalName': 'br', 'newName': 'br element'}); tree.reconstructActiveFormattingElements(); tree.insertElement( - StartTagToken("br", data: LinkedHashMap())); + StartTagToken('br', data: LinkedHashMap())); tree.openElements.removeLast(); } @@ -2219,7 +2250,7 @@ class InBodyPhase extends Phase { tree.generateImpliedEndTags(token.name); if (tree.openElements.last.localName != token.name) { parser.parseError( - token.span, "unexpected-end-tag", {"name": token.name}); + token.span, 'unexpected-end-tag', {'name': token.name}); } while (tree.openElements.removeLast() != node) { // noop @@ -2229,7 +2260,7 @@ class InBodyPhase extends Phase { } else { if (specialElements.contains(getElementNameTuple(node))) { parser.parseError( - token.span, "unexpected-end-tag", {"name": token.name}); + token.span, 'unexpected-end-tag', {'name': token.name}); break; } } @@ -2241,11 +2272,13 @@ class TextPhase extends Phase { TextPhase(parser) : super(parser); // "Tried to process start tag %s in RCDATA/RAWTEXT mode"%token.name + @override // ignore: missing_return Token processStartTag(StartTagToken token) { assert(false); } + @override Token processEndTag(EndTagToken token) { if (token.name == 'script') { endTagScript(token); @@ -2255,14 +2288,16 @@ class TextPhase extends Phase { return null; } + @override Token processCharacters(CharactersToken token) { tree.insertText(token.data, token.span); return null; } + @override bool processEOF() { var last = tree.openElements.last; - parser.parseError(last.sourceSpan, "expected-named-closing-tag-but-got-eof", + parser.parseError(last.sourceSpan, 'expected-named-closing-tag-but-got-eof', {'name': last.localName}); tree.openElements.removeLast(); parser.phase = parser.originalPhase; @@ -2271,7 +2306,7 @@ class TextPhase extends Phase { void endTagScript(EndTagToken token) { var node = tree.openElements.removeLast(); - assert(node.localName == "script"); + assert(node.localName == 'script'); parser.phase = parser.originalPhase; //The rest of this method is all stuff that only happens if //document.write works @@ -2287,36 +2322,37 @@ class InTablePhase extends Phase { // http://www.whatwg.org/specs/web-apps/current-work///in-table InTablePhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "caption": + case 'caption': startTagCaption(token); return null; - case "colgroup": + case 'colgroup': startTagColgroup(token); return null; - case "col": + case 'col': return startTagCol(token); - case "tbody": - case "tfoot": - case "thead": + case 'tbody': + case 'tfoot': + case 'thead': startTagRowGroup(token); return null; - case "td": - case "th": - case "tr": + case 'td': + case 'th': + case 'tr': return startTagImplyTbody(token); - case "table": + case 'table': return startTagTable(token); - case "style": - case "script": + case 'style': + case 'script': return startTagStyleScript(token); - case "input": + case 'input': startTagInput(token); return null; - case "form": + case 'form': startTagForm(token); return null; default: @@ -2325,22 +2361,23 @@ class InTablePhase extends Phase { } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "table": + case 'table': endTagTable(token); return null; - case "body": - case "caption": - case "col": - case "colgroup": - case "html": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": + case 'body': + case 'caption': + case 'col': + case 'colgroup': + case 'html': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': endTagIgnore(token); return null; default: @@ -2351,9 +2388,9 @@ class InTablePhase extends Phase { // helper methods void clearStackToTableContext() { - // "clear the stack back to a table context" - while (tree.openElements.last.localName != "table" && - tree.openElements.last.localName != "html") { + // 'clear the stack back to a table context' + while (tree.openElements.last.localName != 'table' && + tree.openElements.last.localName != 'html') { //parser.parseError(token.span, "unexpected-implied-end-tag-in-table", // {"name": tree.openElements.last.name}) tree.openElements.removeLast(); @@ -2362,10 +2399,11 @@ class InTablePhase extends Phase { } // processing methods + @override bool processEOF() { var last = tree.openElements.last; - if (last.localName != "html") { - parser.parseError(last.sourceSpan, "eof-in-table"); + if (last.localName != 'html') { + parser.parseError(last.sourceSpan, 'eof-in-table'); } else { assert(parser.innerHTMLMode); } @@ -2373,6 +2411,7 @@ class InTablePhase extends Phase { return false; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { var originalPhase = parser.phase; parser.phase = parser._inTableTextPhase; @@ -2381,6 +2420,7 @@ class InTablePhase extends Phase { return null; } + @override Token processCharacters(CharactersToken token) { var originalPhase = parser.phase; parser.phase = parser._inTableTextPhase; @@ -2412,7 +2452,7 @@ class InTablePhase extends Phase { Token startTagCol(StartTagToken token) { startTagColgroup( - StartTagToken("colgroup", data: LinkedHashMap())); + StartTagToken('colgroup', data: LinkedHashMap())); return token; } @@ -2424,14 +2464,14 @@ class InTablePhase extends Phase { Token startTagImplyTbody(StartTagToken token) { startTagRowGroup( - StartTagToken("tbody", data: LinkedHashMap())); + StartTagToken('tbody', data: LinkedHashMap())); return token; } Token startTagTable(StartTagToken token) { - parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", - {"startName": "table", "endName": "table"}); - parser.phase.processEndTag(EndTagToken("table")); + parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag', + {'startName': 'table', 'endName': 'table'}); + parser.phase.processEndTag(EndTagToken('table')); if (!parser.innerHTMLMode) { return token; } @@ -2443,8 +2483,8 @@ class InTablePhase extends Phase { } void startTagInput(StartTagToken token) { - if (asciiUpper2Lower(token.data["type"]) == "hidden") { - parser.parseError(token.span, "unexpected-hidden-input-in-table"); + if (asciiUpper2Lower(token.data['type']) == 'hidden') { + parser.parseError(token.span, 'unexpected-hidden-input-in-table'); tree.insertElement(token); // XXX associate with form tree.openElements.removeLast(); @@ -2454,7 +2494,7 @@ class InTablePhase extends Phase { } void startTagForm(StartTagToken token) { - parser.parseError(token.span, "unexpected-form-in-table"); + parser.parseError(token.span, 'unexpected-form-in-table'); if (tree.formPointer == null) { tree.insertElement(token); tree.formPointer = tree.openElements.last; @@ -2463,8 +2503,8 @@ class InTablePhase extends Phase { } void startTagOther(StartTagToken token) { - parser.parseError(token.span, "unexpected-start-tag-implies-table-voodoo", - {"name": token.name}); + parser.parseError(token.span, 'unexpected-start-tag-implies-table-voodoo', + {'name': token.name}); // Do the table magic! tree.insertFromTable = true; parser._inBodyPhase.processStartTag(token); @@ -2472,14 +2512,14 @@ class InTablePhase extends Phase { } void endTagTable(EndTagToken token) { - if (tree.elementInScope("table", variant: "table")) { + if (tree.elementInScope('table', variant: 'table')) { tree.generateImpliedEndTags(); var last = tree.openElements.last; - if (last.localName != "table") { - parser.parseError(token.span, "end-tag-too-early-named", - {"gotName": "table", "expectedName": last.localName}); + if (last.localName != 'table') { + parser.parseError(token.span, 'end-tag-too-early-named', + {'gotName': 'table', 'expectedName': last.localName}); } - while (tree.openElements.last.localName != "table") { + while (tree.openElements.last.localName != 'table') { tree.openElements.removeLast(); } var node = tree.openElements.removeLast(); @@ -2488,17 +2528,17 @@ class InTablePhase extends Phase { } else { // innerHTML case assert(parser.innerHTMLMode); - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); } } void endTagIgnore(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } void endTagOther(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag-implies-table-voodoo", - {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag-implies-table-voodoo', + {'name': token.name}); // Do the table magic! tree.insertFromTable = true; parser._inBodyPhase.processEndTag(token); @@ -2533,26 +2573,30 @@ class InTableTextPhase extends Phase { characterTokens = []; } + @override Token processComment(CommentToken token) { flushCharacters(); parser.phase = originalPhase; return token; } + @override bool processEOF() { flushCharacters(); parser.phase = originalPhase; return true; } + @override Token processCharacters(CharactersToken token) { - if (token.data == "\u0000") { + if (token.data == '\u0000') { return null; } characterTokens.add(token); return null; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { //pretty sure we should never reach here characterTokens.add(token); @@ -2560,12 +2604,14 @@ class InTableTextPhase extends Phase { return null; } + @override Token processStartTag(StartTagToken token) { flushCharacters(); parser.phase = originalPhase; return token; } + @override Token processEndTag(EndTagToken token) { flushCharacters(); parser.phase = originalPhase; @@ -2577,42 +2623,44 @@ class InCaptionPhase extends Phase { // http://www.whatwg.org/specs/web-apps/current-work///in-caption InCaptionPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "caption": - case "col": - case "colgroup": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': return startTagTableElement(token); default: return startTagOther(token); } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "caption": + case 'caption': endTagCaption(token); return null; - case "table": + case 'table': return endTagTable(token); - case "body": - case "col": - case "colgroup": - case "html": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": + case 'body': + case 'col': + case 'colgroup': + case 'html': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': endTagIgnore(token); return null; default: @@ -2621,23 +2669,25 @@ class InCaptionPhase extends Phase { } bool ignoreEndTagCaption() { - return !tree.elementInScope("caption", variant: "table"); + return !tree.elementInScope('caption', variant: 'table'); } + @override bool processEOF() { parser._inBodyPhase.processEOF(); return false; } + @override Token processCharacters(CharactersToken token) { return parser._inBodyPhase.processCharacters(token); } Token startTagTableElement(StartTagToken token) { - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); //XXX Have to duplicate logic here to find out if the tag is ignored var ignoreEndTag = ignoreEndTagCaption(); - parser.phase.processEndTag(EndTagToken("caption")); + parser.phase.processEndTag(EndTagToken('caption')); if (!ignoreEndTag) { return token; } @@ -2652,13 +2702,13 @@ class InCaptionPhase extends Phase { if (!ignoreEndTagCaption()) { // AT this code is quite similar to endTagTable in "InTable" tree.generateImpliedEndTags(); - if (tree.openElements.last.localName != "caption") { - parser.parseError(token.span, "expected-one-end-tag-but-got-another", { - "gotName": "caption", - "expectedName": tree.openElements.last.localName + if (tree.openElements.last.localName != 'caption') { + parser.parseError(token.span, 'expected-one-end-tag-but-got-another', { + 'gotName': 'caption', + 'expectedName': tree.openElements.last.localName }); } - while (tree.openElements.last.localName != "caption") { + while (tree.openElements.last.localName != 'caption') { tree.openElements.removeLast(); } var node = tree.openElements.removeLast(); @@ -2668,14 +2718,14 @@ class InCaptionPhase extends Phase { } else { // innerHTML case assert(parser.innerHTMLMode); - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); } } Token endTagTable(EndTagToken token) { - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); var ignoreEndTag = ignoreEndTagCaption(); - parser.phase.processEndTag(EndTagToken("caption")); + parser.phase.processEndTag(EndTagToken('caption')); if (!ignoreEndTag) { return token; } @@ -2683,7 +2733,7 @@ class InCaptionPhase extends Phase { } void endTagIgnore(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } Token endTagOther(EndTagToken token) { @@ -2695,11 +2745,12 @@ class InColumnGroupPhase extends Phase { // http://www.whatwg.org/specs/web-apps/current-work///in-column InColumnGroupPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "col": + case 'col': startTagCol(token); return null; default: @@ -2707,12 +2758,13 @@ class InColumnGroupPhase extends Phase { } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "colgroup": + case 'colgroup': endTagColgroup(token); return null; - case "col": + case 'col': endTagCol(token); return null; default: @@ -2721,23 +2773,25 @@ class InColumnGroupPhase extends Phase { } bool ignoreEndTagColgroup() { - return tree.openElements.last.localName == "html"; + return tree.openElements.last.localName == 'html'; } + @override bool processEOF() { var ignoreEndTag = ignoreEndTagColgroup(); if (ignoreEndTag) { assert(parser.innerHTMLMode); return false; } else { - endTagColgroup(EndTagToken("colgroup")); + endTagColgroup(EndTagToken('colgroup')); return true; } } + @override Token processCharacters(CharactersToken token) { var ignoreEndTag = ignoreEndTagColgroup(); - endTagColgroup(EndTagToken("colgroup")); + endTagColgroup(EndTagToken('colgroup')); return ignoreEndTag ? null : token; } @@ -2748,7 +2802,7 @@ class InColumnGroupPhase extends Phase { Token startTagOther(StartTagToken token) { var ignoreEndTag = ignoreEndTagColgroup(); - endTagColgroup(EndTagToken("colgroup")); + endTagColgroup(EndTagToken('colgroup')); return ignoreEndTag ? null : token; } @@ -2756,7 +2810,7 @@ class InColumnGroupPhase extends Phase { if (ignoreEndTagColgroup()) { // innerHTML case assert(parser.innerHTMLMode); - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); } else { var node = tree.openElements.removeLast(); node.endSourceSpan = token.span; @@ -2765,12 +2819,12 @@ class InColumnGroupPhase extends Phase { } void endTagCol(EndTagToken token) { - parser.parseError(token.span, "no-end-tag", {"name": "col"}); + parser.parseError(token.span, 'no-end-tag', {'name': 'col'}); } Token endTagOther(EndTagToken token) { var ignoreEndTag = ignoreEndTagColgroup(); - endTagColgroup(EndTagToken("colgroup")); + endTagColgroup(EndTagToken('colgroup')); return ignoreEndTag ? null : token; } } @@ -2779,45 +2833,47 @@ class InTableBodyPhase extends Phase { // http://www.whatwg.org/specs/web-apps/current-work///in-table0 InTableBodyPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "tr": + case 'tr': startTagTr(token); return null; - case "td": - case "th": + case 'td': + case 'th': return startTagTableCell(token); - case "caption": - case "col": - case "colgroup": - case "tbody": - case "tfoot": - case "thead": + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'tfoot': + case 'thead': return startTagTableOther(token); default: return startTagOther(token); } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "tbody": - case "tfoot": - case "thead": + case 'tbody': + case 'tfoot': + case 'thead': endTagTableRowGroup(token); return null; - case "table": + case 'table': return endTagTable(token); - case "body": - case "caption": - case "col": - case "colgroup": - case "html": - case "td": - case "th": - case "tr": + case 'body': + case 'caption': + case 'col': + case 'colgroup': + case 'html': + case 'td': + case 'th': + case 'tr': endTagIgnore(token); return null; default: @@ -2827,27 +2883,30 @@ class InTableBodyPhase extends Phase { // helper methods void clearStackToTableBodyContext() { - var tableTags = const ["tbody", "tfoot", "thead", "html"]; + var tableTags = const ['tbody', 'tfoot', 'thead', 'html']; while (!tableTags.contains(tree.openElements.last.localName)) { //XXX parser.parseError(token.span, "unexpected-implied-end-tag-in-table", // {"name": tree.openElements.last.name}) tree.openElements.removeLast(); } - if (tree.openElements.last.localName == "html") { + if (tree.openElements.last.localName == 'html') { assert(parser.innerHTMLMode); } } // the rest + @override bool processEOF() { parser._inTablePhase.processEOF(); return false; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { return parser._inTablePhase.processSpaceCharacters(token); } + @override Token processCharacters(CharactersToken token) { return parser._inTablePhase.processCharacters(token); } @@ -2860,8 +2919,8 @@ class InTableBodyPhase extends Phase { Token startTagTableCell(StartTagToken token) { parser.parseError( - token.span, "unexpected-cell-in-table-body", {"name": token.name}); - startTagTr(StartTagToken("tr", data: LinkedHashMap())); + token.span, 'unexpected-cell-in-table-body', {'name': token.name}); + startTagTr(StartTagToken('tr', data: LinkedHashMap())); return token; } @@ -2872,36 +2931,36 @@ class InTableBodyPhase extends Phase { } void endTagTableRowGroup(EndTagToken token) { - if (tree.elementInScope(token.name, variant: "table")) { + if (tree.elementInScope(token.name, variant: 'table')) { clearStackToTableBodyContext(); var node = tree.openElements.removeLast(); node.endSourceSpan = token.span; parser.phase = parser._inTablePhase; } else { parser.parseError( - token.span, "unexpected-end-tag-in-table-body", {"name": token.name}); + token.span, 'unexpected-end-tag-in-table-body', {'name': token.name}); } } Token endTagTable(TagToken token) { // XXX AT Any ideas on how to share this with endTagTable? - if (tree.elementInScope("tbody", variant: "table") || - tree.elementInScope("thead", variant: "table") || - tree.elementInScope("tfoot", variant: "table")) { + if (tree.elementInScope('tbody', variant: 'table') || + tree.elementInScope('thead', variant: 'table') || + tree.elementInScope('tfoot', variant: 'table')) { clearStackToTableBodyContext(); endTagTableRowGroup(EndTagToken(tree.openElements.last.localName)); return token; } else { // innerHTML case assert(parser.innerHTMLMode); - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); } return null; } void endTagIgnore(EndTagToken token) { parser.parseError( - token.span, "unexpected-end-tag-in-table-body", {"name": token.name}); + token.span, 'unexpected-end-tag-in-table-body', {'name': token.name}); } Token endTagOther(EndTagToken token) { @@ -2913,45 +2972,47 @@ class InRowPhase extends Phase { // http://www.whatwg.org/specs/web-apps/current-work///in-row InRowPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "td": - case "th": + case 'td': + case 'th': startTagTableCell(token); return null; - case "caption": - case "col": - case "colgroup": - case "tbody": - case "tfoot": - case "thead": - case "tr": + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'tfoot': + case 'thead': + case 'tr': return startTagTableOther(token); default: return startTagOther(token); } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "tr": + case 'tr': endTagTr(token); return null; - case "table": + case 'table': return endTagTable(token); - case "tbody": - case "tfoot": - case "thead": + case 'tbody': + case 'tfoot': + case 'thead': return endTagTableRowGroup(token); - case "body": - case "caption": - case "col": - case "colgroup": - case "html": - case "td": - case "th": + case 'body': + case 'caption': + case 'col': + case 'colgroup': + case 'html': + case 'td': + case 'th': endTagIgnore(token); return null; default: @@ -2963,30 +3024,33 @@ class InRowPhase extends Phase { void clearStackToTableRowContext() { while (true) { var last = tree.openElements.last; - if (last.localName == "tr" || last.localName == "html") break; + if (last.localName == 'tr' || last.localName == 'html') break; parser.parseError( last.sourceSpan, - "unexpected-implied-end-tag-in-table-row", - {"name": tree.openElements.last.localName}); + 'unexpected-implied-end-tag-in-table-row', + {'name': tree.openElements.last.localName}); tree.openElements.removeLast(); } } bool ignoreEndTagTr() { - return !tree.elementInScope("tr", variant: "table"); + return !tree.elementInScope('tr', variant: 'table'); } // the rest + @override bool processEOF() { parser._inTablePhase.processEOF(); return false; } + @override Token processSpaceCharacters(SpaceCharactersToken token) { return parser._inTablePhase.processSpaceCharacters(token); } + @override Token processCharacters(CharactersToken token) { return parser._inTablePhase.processCharacters(token); } @@ -2999,8 +3063,8 @@ class InRowPhase extends Phase { } Token startTagTableOther(StartTagToken token) { - bool ignoreEndTag = ignoreEndTagTr(); - endTagTr(EndTagToken("tr")); + var ignoreEndTag = ignoreEndTagTr(); + endTagTr(EndTagToken('tr')); // XXX how are we sure it's always ignored in the innerHTML case? return ignoreEndTag ? null : token; } @@ -3018,31 +3082,31 @@ class InRowPhase extends Phase { } else { // innerHTML case assert(parser.innerHTMLMode); - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); } } Token endTagTable(EndTagToken token) { var ignoreEndTag = ignoreEndTagTr(); - endTagTr(EndTagToken("tr")); + endTagTr(EndTagToken('tr')); // Reprocess the current tag if the tr end tag was not ignored // XXX how are we sure it's always ignored in the innerHTML case? return ignoreEndTag ? null : token; } Token endTagTableRowGroup(EndTagToken token) { - if (tree.elementInScope(token.name, variant: "table")) { - endTagTr(EndTagToken("tr")); + if (tree.elementInScope(token.name, variant: 'table')) { + endTagTr(EndTagToken('tr')); return token; } else { - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); return null; } } void endTagIgnore(EndTagToken token) { parser.parseError( - token.span, "unexpected-end-tag-in-table-row", {"name": token.name}); + token.span, 'unexpected-end-tag-in-table-row', {'name': token.name}); } Token endTagOther(EndTagToken token) { @@ -3054,43 +3118,45 @@ class InCellPhase extends Phase { // http://www.whatwg.org/specs/web-apps/current-work///in-cell InCellPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "caption": - case "col": - case "colgroup": - case "tbody": - case "td": - case "tfoot": - case "th": - case "thead": - case "tr": + case 'caption': + case 'col': + case 'colgroup': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': return startTagTableOther(token); default: return startTagOther(token); } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "td": - case "th": + case 'td': + case 'th': endTagTableCell(token); return null; - case "body": - case "caption": - case "col": - case "colgroup": - case "html": + case 'body': + case 'caption': + case 'col': + case 'colgroup': + case 'html': endTagIgnore(token); return null; - case "table": - case "tbody": - case "tfoot": - case "thead": - case "tr": + case 'table': + case 'tbody': + case 'tfoot': + case 'thead': + case 'tr': return endTagImply(token); default: return endTagOther(token); @@ -3099,32 +3165,34 @@ class InCellPhase extends Phase { // helper void closeCell() { - if (tree.elementInScope("td", variant: "table")) { - endTagTableCell(EndTagToken("td")); - } else if (tree.elementInScope("th", variant: "table")) { - endTagTableCell(EndTagToken("th")); + if (tree.elementInScope('td', variant: 'table')) { + endTagTableCell(EndTagToken('td')); + } else if (tree.elementInScope('th', variant: 'table')) { + endTagTableCell(EndTagToken('th')); } } // the rest + @override bool processEOF() { parser._inBodyPhase.processEOF(); return false; } + @override Token processCharacters(CharactersToken token) { return parser._inBodyPhase.processCharacters(token); } Token startTagTableOther(StartTagToken token) { - if (tree.elementInScope("td", variant: "table") || - tree.elementInScope("th", variant: "table")) { + if (tree.elementInScope('td', variant: 'table') || + tree.elementInScope('th', variant: 'table')) { closeCell(); return token; } else { // innerHTML case assert(parser.innerHTMLMode); - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); return null; } } @@ -3134,11 +3202,11 @@ class InCellPhase extends Phase { } void endTagTableCell(EndTagToken token) { - if (tree.elementInScope(token.name, variant: "table")) { + if (tree.elementInScope(token.name, variant: 'table')) { tree.generateImpliedEndTags(token.name); if (tree.openElements.last.localName != token.name) { parser.parseError( - token.span, "unexpected-cell-end-tag", {"name": token.name}); + token.span, 'unexpected-cell-end-tag', {'name': token.name}); popOpenElementsUntil(token); } else { var node = tree.openElements.removeLast(); @@ -3147,21 +3215,21 @@ class InCellPhase extends Phase { tree.clearActiveFormattingElements(); parser.phase = parser._inRowPhase; } else { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } } void endTagIgnore(EndTagToken token) { - parser.parseError(token.span, "unexpected-end-tag", {"name": token.name}); + parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name}); } Token endTagImply(EndTagToken token) { - if (tree.elementInScope(token.name, variant: "table")) { + if (tree.elementInScope(token.name, variant: 'table')) { closeCell(); return token; } else { // sometimes innerHTML case - parser.parseError(token.span, "undefined-error"); + parser.parseError(token.span, 'undefined-error'); } return null; } @@ -3174,39 +3242,41 @@ class InCellPhase extends Phase { class InSelectPhase extends Phase { InSelectPhase(parser) : super(parser); + @override Token processStartTag(StartTagToken token) { switch (token.name) { - case "html": + case 'html': return startTagHtml(token); - case "option": + case 'option': startTagOption(token); return null; - case "optgroup": + case 'optgroup': startTagOptgroup(token); return null; - case "select": + case 'select': startTagSelect(token); return null; - case "input": - case "keygen": - case "textarea": + case 'input': + case 'keygen': + case 'textarea': return startTagInput(token); - case "script": + case 'script': return startTagScript(token); default: return startTagOther(token); } } + @override Token processEndTag(EndTagToken token) { switch (token.name) { - case "option": + case 'option': endTagOption(token); return null; - case "optgroup": + case 'optgroup': endTagOptgroup(token); return null; - case "select": + case 'select': endTagSelect(token); return null; default: @@ -3216,18 +3286,20 @@ class InSelectPhase extends Phase { } // http://www.whatwg.org/specs/web-apps/current-work///in-select + @override bool processEOF() { var last = tree.openElements.last; - if (last.localName != "html") { - parser.parseError(last.sourceSpan, "eof-in-select"); + if (last.localName != 'html') { + parser.parseError(last.sourceSpan, 'eof-in-select'); } else { assert(parser.innerHTMLMode); } return false; } + @override Token processCharacters(CharactersToken token) { - if (token.data == "\u0000") { + if (token.data == '\u0000') { return null; } tree.insertText(token.data, token.span); @@ -3236,31 +3308,31 @@ class InSelectPhase extends Phase { void startTagOption(StartTagToken token) { // We need to imply if