Merge branch 'master' into null_safety-migration
diff --git a/.travis.yml b/.travis.yml
index 497fe68..9865545 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,26 +2,17 @@
dart:
- dev
- - 2.8.1
-
-dart_task:
- - test: -p vm
- - test: -p chrome
matrix:
include:
- - dart: dev
- dart_task: dartfmt
- - dart: dev
- dart_task:
+ - script: pub run test -p vm,chrome
+ - dart_task: dartfmt
+ - dart_task:
dartanalyzer: --fatal-warnings --fatal-infos .
- - dart: 2.8.1
- dart_task:
- dartanalyzer: --fatal-warnings .
# Only building master means that we don't run two builds for each pull request.
branches:
- only: [master]
+ only: [master, null_safety]
cache:
directories:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e294c41..2269d1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 0.15.0-dev
+## 0.15.0-nullsafety-dev
- Drop `HtmlParser.lastPhase` and `HtmlParser.beforeRcDataPhase`. These fields
never had a value other than `null`.
diff --git a/lib/dom.dart b/lib/dom.dart
index 1950ffe..2f2496d 100644
--- a/lib/dom.dart
+++ b/lib/dom.dart
@@ -26,7 +26,7 @@
// that exposes namespace info.
class AttributeName implements Comparable<Object> {
/// The namespace prefix, e.g. `xlink`.
- final String prefix;
+ final String? prefix;
/// The attribute name, e.g. `title`.
final String name;
@@ -58,7 +58,7 @@
int compareTo(Object other) {
// Not sure about this sort order
if (other is! AttributeName) return 1;
- final otherAttributeName = other as AttributeName;
+ final otherAttributeName = other;
var cmp = (prefix ?? '').compareTo((otherAttributeName.prefix ?? ''));
if (cmp != 0) return cmp;
cmp = name.compareTo(otherAttributeName.name);
@@ -85,7 +85,8 @@
/// are implemented. For example, nth-child does not implement An+B syntax
/// and *-of-type is not implemented. If a selector is not implemented this
/// method will throw [UnimplementedError].
- Element querySelector(String selector) => query.querySelector(this, selector);
+ Element? querySelector(String selector) =>
+ query.querySelector(this, selector);
/// Returns all descendant nodes matching the given selectors, using a
/// preorder traversal.
@@ -102,7 +103,7 @@
// http://dom.spec.whatwg.org/#interface-nonelementparentnode
abstract class _NonElementParentNode implements _ParentNode {
// TODO(jmesserly): could be faster, should throw on invalid id.
- Element getElementById(String id) => querySelector('#$id');
+ Element? getElementById(String id) => querySelector('#$id');
}
// This doesn't exist as an interface in the spec, but it's useful to merge
@@ -136,13 +137,13 @@
static const int TEXT_NODE = 3;
/// The parent of the current node (or null for the document node).
- Node parentNode;
+ Node? parentNode;
/// The parent element of this node.
///
/// Returns null if this node either does not have a parent or its parent is
/// not an element.
- Element get parent {
+ Element? get parent {
final parentNode = this.parentNode;
return parentNode is Element ? parentNode : null;
}
@@ -156,28 +157,26 @@
/// A list of child nodes of the current node. This must
/// include all elements but not necessarily other node types.
- final NodeList nodes = NodeList._();
+ late final nodes = NodeList._(this);
- List<Element> _elements;
+ List<Element>? _elements;
// TODO(jmesserly): consider using an Expando for this, and put it in
// dom_parsing. Need to check the performance affect.
/// The source span of this node, if it was created by the [HtmlParser].
- FileSpan sourceSpan;
+ FileSpan? sourceSpan;
/// The attribute spans if requested. Otherwise null.
- LinkedHashMap<Object, FileSpan> _attributeSpans;
- LinkedHashMap<Object, FileSpan> _attributeValueSpans;
+ LinkedHashMap<Object, FileSpan>? _attributeSpans;
+ LinkedHashMap<Object, FileSpan>? _attributeValueSpans;
- Node._() {
- nodes._parent = this;
- }
+ Node._();
/// If [sourceSpan] is available, this contains the spans of each attribute.
/// The span of an attribute is the entire attribute, including the name and
/// quotes (if any). For example, the span of "attr" in `<a attr="value">`
/// would be the text `attr="value"`.
- LinkedHashMap<Object, FileSpan> get attributeSpans {
+ LinkedHashMap<Object, FileSpan>? get attributeSpans {
_ensureAttributeSpans();
return _attributeSpans;
}
@@ -186,7 +185,7 @@
/// value. Unlike [attributeSpans], this span will include only the value.
/// For example, the value span of "attr" in `<a attr="value">` would be the
/// text `value`.
- LinkedHashMap<Object, FileSpan> get attributeValueSpans {
+ LinkedHashMap<Object, FileSpan>? get attributeValueSpans {
_ensureAttributeSpans();
return _attributeValueSpans;
}
@@ -215,12 +214,12 @@
}
// Implemented per: http://dom.spec.whatwg.org/#dom-node-textcontent
- String get text => null;
- set text(String value) {}
+ String? get text => null;
+ set text(String? value) {}
void append(Node node) => nodes.add(node);
- Node get firstChild => nodes.isNotEmpty ? nodes[0] : null;
+ Node? get firstChild => nodes.isNotEmpty ? nodes[0] : null;
void _addOuterHtml(StringBuffer str);
@@ -232,18 +231,12 @@
Node remove() {
// TODO(jmesserly): is parent == null an error?
- if (parentNode != null) {
- parentNode.nodes.remove(this);
- }
+ parentNode?.nodes.remove(this);
return this;
}
/// Insert [node] as a child of the current node, before [refNode] in the
- /// list of child nodes.
- ///
- /// [refNode] must be a hild of the current node or null. If [refNode] is null
- /// [node] will be added to the end of the list.
- void insertBefore(Node node, Node refNode) {
+ void insertBefore(Node node, Node? refNode) {
if (refNode == null) {
nodes.add(node);
} else {
@@ -256,7 +249,7 @@
if (parentNode == null) {
throw UnsupportedError('Node must have a parent to replace it.');
}
- parentNode.nodes[parentNode.nodes.indexOf(this)] = otherNode;
+ parentNode!.nodes[parentNode!.nodes.indexOf(this)] = otherNode;
return this;
}
@@ -280,12 +273,13 @@
void _ensureAttributeSpans() {
if (_attributeSpans != null) return;
- _attributeSpans = LinkedHashMap<Object, FileSpan>();
- _attributeValueSpans = LinkedHashMap<Object, FileSpan>();
+ final attributeSpans = _attributeSpans = LinkedHashMap<Object, FileSpan>();
+ final attributeValueSpans =
+ _attributeValueSpans = LinkedHashMap<Object, FileSpan>();
if (sourceSpan == null) return;
- final tokenizer = HtmlTokenizer(sourceSpan.text,
+ final tokenizer = HtmlTokenizer(sourceSpan!.text,
generateSpans: true, attributeSpans: true);
tokenizer.moveNext();
@@ -293,13 +287,14 @@
if (token.attributeSpans == null) return; // no attributes
- for (var attr in token.attributeSpans) {
- final offset = sourceSpan.start.offset;
- _attributeSpans[attr.name] =
- sourceSpan.file.span(offset + attr.start, offset + attr.end);
+ for (var attr in token.attributeSpans!) {
+ final offset = sourceSpan!.start.offset;
+ final name = attr.name!;
+ attributeSpans[name] =
+ sourceSpan!.file.span(offset + attr.start, offset + attr.end);
if (attr.startValue != null) {
- _attributeValueSpans[attr.name] = sourceSpan.file
- .span(offset + attr.startValue, offset + attr.endValue);
+ attributeValueSpans[name] = sourceSpan!.file
+ .span(offset + attr.startValue!, offset + attr.endValue);
}
}
}
@@ -323,9 +318,9 @@
int get nodeType => Node.DOCUMENT_NODE;
// TODO(jmesserly): optmize this if needed
- Element get documentElement => querySelector('html');
- Element get head => documentElement.querySelector('head');
- Element get body => documentElement.querySelector('body');
+ Element get documentElement => querySelector('html')!;
+ Element get head => documentElement.querySelector('head')!;
+ Element get body => documentElement.querySelector('body')!;
/// Returns a fragment of HTML or XML that represents the element and its
/// contents.
@@ -348,7 +343,7 @@
// TODO(jmesserly): this is only a partial implementation of:
// http://dom.spec.whatwg.org/#dom-document-createelementns
- Element createElementNS(String namespaceUri, String tag) {
+ Element createElementNS(String? namespaceUri, String? tag) {
if (namespaceUri == '') namespaceUri = null;
return Element._(tag, namespaceUri);
}
@@ -381,15 +376,15 @@
void _addOuterHtml(StringBuffer str) => _addInnerHtml(str);
@override
- String get text => _getText(this);
+ String? get text => _getText(this);
@override
- set text(String value) => _setText(this, value);
+ set text(String? value) => _setText(this, value);
}
class DocumentType extends Node {
- final String name;
- final String publicId;
- final String systemId;
+ final String? name;
+ final String? publicId;
+ final String? systemId;
DocumentType(this.name, this.publicId, this.systemId) : super._();
@@ -424,7 +419,7 @@
/// It will flatten back to a String on read.
Object _data;
- Text(String data)
+ Text(String? data)
: _data = data ?? '',
super._();
@@ -433,7 +428,7 @@
String get data => _data = _data.toString();
set data(String value) {
- _data = value ?? '';
+ _data = identical(value, null) ? '' : value;
}
@override
@@ -454,24 +449,24 @@
@override
String get text => data;
@override
- set text(String value) {
+ set text(covariant String value) {
data = value;
}
}
// TODO(jmesserly): Elements should have a pointer back to their document
class Element extends Node with _ParentNode, _ElementAndDocument {
- final String namespaceUri;
+ final String? namespaceUri;
/// The [local name](http://dom.spec.whatwg.org/#concept-element-local-name)
/// of this element.
- final String localName;
+ final String? localName;
// TODO(jmesserly): consider using an Expando for this, and put it in
// dom_parsing. Need to check the performance affect.
/// The source span of the end tag this element, if it was created by the
/// [HtmlParser]. May be `null` if does not have an implicit end tag.
- FileSpan endSourceSpan;
+ FileSpan? endSourceSpan;
Element._(this.localName, [this.namespaceUri]) : super._();
@@ -508,12 +503,12 @@
// 3) Verify that the html does not contain both <head> and <body> tags.
// 4) Detach the created element from its dummy parent.
var parentTag = 'div';
- String tag;
+ String? tag;
final match = _startTagRegexp.firstMatch(html);
if (match != null) {
- tag = match.group(1).toLowerCase();
+ tag = match.group(1)!.toLowerCase();
if (_customParentTagMap.containsKey(tag)) {
- parentTag = _customParentTagMap[tag];
+ parentTag = _customParentTagMap[tag]!;
}
}
@@ -536,9 +531,9 @@
int get nodeType => Node.ELEMENT_NODE;
// TODO(jmesserly): we can make this faster
- Element get previousElementSibling {
+ Element? get previousElementSibling {
if (parentNode == null) return null;
- final siblings = parentNode.nodes;
+ final siblings = parentNode!.nodes;
for (var i = siblings.indexOf(this) - 1; i >= 0; i--) {
final s = siblings[i];
if (s is Element) return s;
@@ -546,7 +541,8 @@
return null;
}
- Element get nextElementSibling {
+ Element? get nextElementSibling {
+ final parentNode = this.parentNode;
if (parentNode == null) return null;
final siblings = parentNode.nodes;
for (var i = siblings.indexOf(this) + 1; i < siblings.length; i++) {
@@ -565,7 +561,7 @@
@override
String get text => _getText(this);
@override
- set text(String value) => _setText(this, value);
+ set text(String? value) => _setText(this, value);
/// Returns a fragment of HTML or XML that represents the element and its
/// contents.
@@ -581,7 +577,7 @@
nodes.clear();
// TODO(jmesserly): should be able to get the same effect by adding the
// fragment directly.
- nodes.addAll(parseFragment(value, container: localName).nodes);
+ nodes.addAll(parseFragment(value, container: localName!).nodes);
}
@override
@@ -626,7 +622,7 @@
if (!isVoidElement(localName)) str.write('</$localName>');
}
- static String _getSerializationPrefix(String uri) {
+ static String _getSerializationPrefix(String? uri) {
if (uri == null ||
uri == Namespaces.html ||
uri == Namespaces.mathml ||
@@ -678,7 +674,7 @@
}
class Comment extends Node {
- String data;
+ String? data;
Comment(this.data) : super._();
@@ -697,9 +693,9 @@
Comment clone(bool deep) => Comment(data);
@override
- String get text => data;
+ String? get text => data;
@override
- set text(String value) {
+ set text(String? value) {
data = value;
}
}
@@ -708,11 +704,9 @@
// (The requirement to remove the node from the old node list makes it tricky.)
// TODO(jmesserly): is there any way to share code with the _NodeListImpl?
class NodeList extends ListProxy<Node> {
- // Note: this is conceptually final, but because of circular reference
- // between Node and NodeList we initialize it after construction.
- Node _parent;
+ final Node _parent;
- NodeList._();
+ NodeList._(this._parent);
Node _setParent(Node node) {
// Note: we need to remove the node from its previous parent node, if any,
@@ -918,7 +912,7 @@
}
@override
- bool contains(Object element) {
+ bool contains(Object? element) {
return element is Element && _childNodes.contains(element);
}
@@ -926,7 +920,7 @@
Iterable<Element> get reversed => _filtered.reversed;
@override
- void sort([int Function(Element, Element) compare]) {
+ void sort([int Function(Element, Element)? compare]) {
throw UnsupportedError('TODO(jacobr): should we impl?');
}
@@ -937,7 +931,7 @@
}
@override
- void fillRange(int start, int end, [Element fillValue]) {
+ void fillRange(int start, int end, [Element? fillValue]) {
throw UnimplementedError();
}
@@ -960,11 +954,7 @@
@override
Element removeLast() {
- final result = last;
- if (result != null) {
- result.remove();
- }
- return result;
+ return last..remove();
}
@override
@@ -992,7 +982,7 @@
}
@override
- bool remove(Object element) {
+ bool remove(Object? element) {
if (element is! Element) return false;
for (var i = 0; i < length; i++) {
final indexElement = this[i];
@@ -1025,18 +1015,19 @@
@override
Set<Element> toSet() => Set<Element>.from(this);
@override
- Element firstWhere(bool Function(Element) test, {Element Function() orElse}) {
+ Element firstWhere(bool Function(Element) test,
+ {Element Function()? orElse}) {
return _filtered.firstWhere(test, orElse: orElse);
}
@override
- Element lastWhere(bool Function(Element) test, {Element Function() orElse}) {
+ Element lastWhere(bool Function(Element) test, {Element Function()? orElse}) {
return _filtered.lastWhere(test, orElse: orElse);
}
@override
Element singleWhere(bool Function(Element) test,
- {Element Function() orElse}) {
+ {Element Function()? orElse}) {
if (orElse != null) throw UnimplementedError('orElse');
return _filtered.singleWhere(test);
}
@@ -1055,17 +1046,17 @@
@override
Iterator<Element> get iterator => _filtered.iterator;
@override
- List<Element> sublist(int start, [int end]) => _filtered.sublist(start, end);
+ List<Element> sublist(int start, [int? end]) => _filtered.sublist(start, end);
@override
Iterable<Element> getRange(int start, int end) =>
_filtered.getRange(start, end);
@override
- int indexOf(Object element, [int start = 0]) =>
+ int indexOf(Object? element, [int start = 0]) =>
// Cast forced by ListMixin https://github.com/dart-lang/sdk/issues/31311
_filtered.indexOf(element as Element, start);
@override
- int lastIndexOf(Object element, [int start]) {
+ int lastIndexOf(Object? element, [int? start]) {
start ??= length - 1;
// Cast forced by ListMixin https://github.com/dart-lang/sdk/issues/31311
return _filtered.lastIndexOf(element as Element, start);
@@ -1085,7 +1076,7 @@
// For Element and DocumentFragment
String _getText(Node node) => (_ConcatTextVisitor()..visit(node)).toString();
-void _setText(Node node, String value) {
+void _setText(Node node, String? value) {
node.nodes.clear();
node.append(Text(value));
}
diff --git a/lib/dom_parsing.dart b/lib/dom_parsing.dart
index f108b59..142222e 100644
--- a/lib/dom_parsing.dart
+++ b/lib/dom_parsing.dart
@@ -111,7 +111,7 @@
@override
void visitComment(Comment node) {
- final data = htmlSerializeEscape(node.data);
+ final data = htmlSerializeEscape(node.data!);
_str.write('<code class="markup comment"><!--$data--></code>');
}
}
@@ -136,10 +136,10 @@
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;
+ StringBuffer? result;
for (var i = 0; i < text.length; i++) {
final ch = text[i];
- String replace;
+ String? replace;
switch (ch) {
case '&':
replace = '&';
@@ -172,7 +172,7 @@
/// This method is useful to a pretty printer, because void elements must not
/// have an end tag.
/// See also: <http://dev.w3.org/html5/markup/syntax.html#void-elements>.
-bool isVoidElement(String tagName) {
+bool isVoidElement(String? tagName) {
switch (tagName) {
case 'area':
case 'base':
diff --git a/lib/parser.dart b/lib/parser.dart
index 5dce969..ca193f0 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -37,7 +37,7 @@
/// can additionally pass [sourceUrl] to indicate where the [input] was
/// extracted from.
Document parse(input,
- {String encoding, bool generateSpans = false, String sourceUrl}) {
+ {String? encoding, bool generateSpans = false, String? sourceUrl}) {
final p = HtmlParser(input,
encoding: encoding, generateSpans: generateSpans, sourceUrl: sourceUrl);
return p.parse();
@@ -57,9 +57,9 @@
/// from.
DocumentFragment parseFragment(input,
{String container = 'div',
- String encoding,
+ String? encoding,
bool generateSpans = false,
- String sourceUrl}) {
+ String? sourceUrl}) {
final p = HtmlParser(input,
encoding: encoding, generateSpans: generateSpans, sourceUrl: sourceUrl);
return p.parseFragment(container);
@@ -80,8 +80,6 @@
final List<ParseError> errors = <ParseError>[];
- String container;
-
bool firstStartTag = false;
// TODO(jmesserly): use enum?
@@ -89,38 +87,43 @@
String compatMode = 'no quirks';
/// innerHTML container when parsing document fragment.
- String innerHTML;
+ String? innerHTML;
- Phase phase;
+ late Phase phase = _initialPhase;
- Phase originalPhase;
+ Phase? originalPhase;
- bool framesetOK;
+ var framesetOK = true;
// These fields hold the different phase singletons. At any given time one
// of them will be active.
- InitialPhase _initialPhase;
- BeforeHtmlPhase _beforeHtmlPhase;
- BeforeHeadPhase _beforeHeadPhase;
- InHeadPhase _inHeadPhase;
- AfterHeadPhase _afterHeadPhase;
- InBodyPhase _inBodyPhase;
- TextPhase _textPhase;
- InTablePhase _inTablePhase;
- InTableTextPhase _inTableTextPhase;
- InCaptionPhase _inCaptionPhase;
- InColumnGroupPhase _inColumnGroupPhase;
- InTableBodyPhase _inTableBodyPhase;
- InRowPhase _inRowPhase;
- InCellPhase _inCellPhase;
- InSelectPhase _inSelectPhase;
- InSelectInTablePhase _inSelectInTablePhase;
- InForeignContentPhase _inForeignContentPhase;
- AfterBodyPhase _afterBodyPhase;
- InFramesetPhase _inFramesetPhase;
- AfterFramesetPhase _afterFramesetPhase;
- AfterAfterBodyPhase _afterAfterBodyPhase;
- AfterAfterFramesetPhase _afterAfterFramesetPhase;
+ late final _initialPhase = InitialPhase(this);
+ late final _beforeHtmlPhase = BeforeHtmlPhase(this);
+ late final _beforeHeadPhase = BeforeHeadPhase(this);
+ late final _inHeadPhase = InHeadPhase(this);
+ // TODO: html5lib did not implement the no script parsing mode
+ // More information here:
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#scripting-flag
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inheadnoscript
+ // late final _inHeadNoscript = InHeadNoScriptPhase(this);
+ late final _afterHeadPhase = AfterHeadPhase(this);
+ late final _inBodyPhase = InBodyPhase(this);
+ late final _textPhase = TextPhase(this);
+ late final _inTablePhase = InTablePhase(this);
+ late final _inTableTextPhase = InTableTextPhase(this);
+ late final _inCaptionPhase = InCaptionPhase(this);
+ late final _inColumnGroupPhase = InColumnGroupPhase(this);
+ late final _inTableBodyPhase = InTableBodyPhase(this);
+ late final _inRowPhase = InRowPhase(this);
+ late final _inCellPhase = InCellPhase(this);
+ late final _inSelectPhase = InSelectPhase(this);
+ late final _inSelectInTablePhase = InSelectInTablePhase(this);
+ late final _inForeignContentPhase = InForeignContentPhase(this);
+ late final _afterBodyPhase = AfterBodyPhase(this);
+ late final _inFramesetPhase = InFramesetPhase(this);
+ late final _afterFramesetPhase = AfterFramesetPhase(this);
+ late final _afterAfterBodyPhase = AfterAfterBodyPhase(this);
+ late final _afterAfterFramesetPhase = AfterAfterFramesetPhase(this);
/// Create an HtmlParser and configure the [tree] builder and [strict] mode.
/// The [input] can be a [String], [List<int>] of bytes or an [HtmlTokenizer].
@@ -138,14 +141,14 @@
/// that standard way to parse HTML is to lowercase, which is what the browser
/// DOM will do if you request `Element.outerHTML`, for example.
HtmlParser(input,
- {String encoding,
+ {String? encoding,
bool parseMeta = true,
bool lowercaseElementName = true,
bool lowercaseAttrName = true,
this.strict = false,
this.generateSpans = false,
- String sourceUrl,
- TreeBuilder tree})
+ String? sourceUrl,
+ TreeBuilder? tree})
: tree = tree ?? TreeBuilder(true),
tokenizer = (input is HtmlTokenizer
? input
@@ -157,33 +160,6 @@
generateSpans: generateSpans,
sourceUrl: sourceUrl)) {
tokenizer.parser = this;
- _initialPhase = InitialPhase(this);
- _beforeHtmlPhase = BeforeHtmlPhase(this);
- _beforeHeadPhase = BeforeHeadPhase(this);
- _inHeadPhase = InHeadPhase(this);
- // TODO(jmesserly): html5lib did not implement the no script parsing mode
- // More information here:
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#scripting-flag
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inheadnoscript
- // "inHeadNoscript": new InHeadNoScriptPhase(this);
- _afterHeadPhase = AfterHeadPhase(this);
- _inBodyPhase = InBodyPhase(this);
- _textPhase = TextPhase(this);
- _inTablePhase = InTablePhase(this);
- _inTableTextPhase = InTableTextPhase(this);
- _inCaptionPhase = InCaptionPhase(this);
- _inColumnGroupPhase = InColumnGroupPhase(this);
- _inTableBodyPhase = InTableBodyPhase(this);
- _inRowPhase = InRowPhase(this);
- _inCellPhase = InCellPhase(this);
- _inSelectPhase = InSelectPhase(this);
- _inSelectInTablePhase = InSelectInTablePhase(this);
- _inForeignContentPhase = InForeignContentPhase(this);
- _afterBodyPhase = AfterBodyPhase(this);
- _inFramesetPhase = InFramesetPhase(this);
- _afterFramesetPhase = AfterFramesetPhase(this);
- _afterAfterBodyPhase = AfterAfterBodyPhase(this);
- _afterAfterFramesetPhase = AfterAfterFramesetPhase(this);
}
bool get innerHTMLMode => innerHTML != null;
@@ -200,7 +176,7 @@
/// 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']) {
- if (container == null) throw ArgumentError('container');
+ ArgumentError.checkNotNull(container, 'container');
innerHTML = container.toLowerCase();
_parse();
return tree.getFragment();
@@ -276,7 +252,7 @@
if (isMathMLTextIntegrationPoint(node)) {
if (type == TokenKind.startTag &&
(token as StartTagToken).name != 'mglyph' &&
- (token as StartTagToken).name != 'malignmark') {
+ token.name != 'malignmark') {
return false;
}
if (type == TokenKind.characters || type == TokenKind.spaceCharacters) {
@@ -304,7 +280,7 @@
void mainLoop() {
while (tokenizer.moveNext()) {
final token = tokenizer.current;
- var newToken = token;
+ Token? newToken = token;
int type;
while (newToken != null) {
type = newToken.kind;
@@ -367,14 +343,12 @@
/// The last span available. Used for EOF errors if we don't have something
/// better.
- SourceSpan get _lastSpan {
- if (tokenizer.stream.fileInfo == null) return null;
- final pos = tokenizer.stream.position;
- return tokenizer.stream.fileInfo.location(pos).pointSpan();
- }
+ SourceSpan? get _lastSpan => tokenizer.stream.fileInfo
+ ?.location(tokenizer.stream.position)
+ .pointSpan();
- void parseError(SourceSpan span, String errorcode,
- [Map datavars = const {}]) {
+ void parseError(SourceSpan? span, String errorcode,
+ [Map? datavars = const {}]) {
if (!generateSpans && span == null) {
span = _lastSpan;
}
@@ -457,9 +431,9 @@
'zoomandpan': 'zoomAndPan'
};
for (var originalName in token.data.keys.toList()) {
- final svgName = replacements[originalName];
+ final svgName = replacements[originalName as String];
if (svgName != null) {
- token.data[svgName] = token.data.remove(originalName);
+ token.data[svgName] = token.data.remove(originalName)!;
}
}
}
@@ -483,9 +457,9 @@
};
for (var originalName in token.data.keys.toList()) {
- final foreignName = replacements[originalName];
+ final foreignName = replacements[originalName as String];
if (foreignName != null) {
- token.data[foreignName] = token.data.remove(originalName);
+ token.data[foreignName] = token.data.remove(originalName)!;
}
}
}
@@ -602,33 +576,33 @@
throw UnimplementedError();
}
- Token processComment(CommentToken token) {
+ Token? processComment(CommentToken token) {
// For most phases the following is correct. Where it's not it will be
// overridden.
tree.insertComment(token, tree.openElements.last);
return null;
}
- Token processDoctype(DoctypeToken token) {
+ Token? processDoctype(DoctypeToken token) {
parser.parseError(token.span, 'unexpected-doctype');
return null;
}
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
tree.insertText(token.data, token.span);
return null;
}
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
tree.insertText(token.data, token.span);
return null;
}
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
throw UnimplementedError();
}
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
if (parser.firstStartTag == false && token.name == 'html') {
parser.parseError(token.span, 'non-html-root');
}
@@ -642,7 +616,7 @@
return null;
}
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
throw UnimplementedError();
}
@@ -653,9 +627,7 @@
while (node.localName != name) {
node = tree.openElements.removeLast();
}
- if (node != null) {
- node.endSourceSpan = token.span;
- }
+ node.endSourceSpan = token.span;
}
}
@@ -663,18 +635,18 @@
InitialPhase(HtmlParser parser) : super(parser);
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return null;
}
@override
- Token processComment(CommentToken token) {
+ Token? processComment(CommentToken token) {
tree.insertComment(token, tree.document);
return null;
}
@override
- Token processDoctype(DoctypeToken token) {
+ Token? processDoctype(DoctypeToken token) {
final name = token.name;
var publicId = token.publicId?.toAsciiLowerCase();
final systemId = token.systemId;
@@ -832,13 +804,13 @@
}
@override
- Token processComment(CommentToken token) {
+ Token? processComment(CommentToken token) {
tree.insertComment(token, tree.document);
return null;
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return null;
}
@@ -859,7 +831,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'head':
case 'body':
@@ -879,7 +851,7 @@
BeforeHeadPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -892,7 +864,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'head':
case 'body':
@@ -912,7 +884,7 @@
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return null;
}
@@ -923,7 +895,7 @@
}
@override
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -953,7 +925,7 @@
InHeadPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -987,7 +959,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'head':
endTagHead(token);
@@ -1016,7 +988,7 @@
}
@override
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -1100,7 +1072,7 @@
AfterHeadPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -1130,7 +1102,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'body':
case 'html':
@@ -1155,7 +1127,7 @@
}
@override
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -1219,7 +1191,7 @@
InBodyPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -1392,7 +1364,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'body':
endTagBody(token);
@@ -1497,7 +1469,7 @@
final element = tree.openElements.last;
final matchingElements = [];
- for (Node node in tree.activeFormattingElements.reversed) {
+ for (Node? node in tree.activeFormattingElements.reversed) {
if (node == null) {
break;
} else if (isMatchingFormattingElement(node as Element, element)) {
@@ -1557,7 +1529,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
if (token.data == '\u0000') {
//The tokenizer should always emit null on its own
return null;
@@ -1571,7 +1543,7 @@
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
if (dropNewline) {
processSpaceCharactersDropNewline(token);
} else {
@@ -1581,7 +1553,7 @@
return null;
}
- Token startTagProcessInHead(StartTagToken token) {
+ Token? startTagProcessInHead(StartTagToken token) {
return parser._inHeadPhase.processStartTag(token);
}
@@ -1605,7 +1577,7 @@
assert(parser.innerHTMLMode);
} else if (parser.framesetOK) {
if (tree.openElements[1].parentNode != null) {
- tree.openElements[1].parentNode.nodes.remove(tree.openElements[1]);
+ tree.openElements[1].parentNode!.nodes.remove(tree.openElements[1]);
}
while (tree.openElements.last.localName != 'html') {
tree.openElements.removeLast();
@@ -1651,7 +1623,7 @@
'dt': ['dt', 'dd'],
'dd': ['dt', 'dd']
};
- final stopNames = stopNamesMap[token.name];
+ final stopNames = stopNamesMap[token.name!]!;
for (var node in tree.openElements.reversed) {
if (stopNames.contains(node.localName)) {
parser.phase.processEndTag(EndTagToken(node.localName));
@@ -1720,7 +1692,7 @@
addFormattingElement(token);
}
- Token startTagButton(StartTagToken token) {
+ Token? startTagButton(StartTagToken token) {
if (tree.elementInScope('button')) {
parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag',
{'startName': 'button', 'endName': 'button'});
@@ -1922,7 +1894,7 @@
token.span, 'unexpected-start-tag-ignored', {'name': token.name});
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
tree.reconstructActiveFormattingElements();
tree.insertElement(token);
return null;
@@ -1978,7 +1950,7 @@
parser.phase = parser._afterBodyPhase;
}
- Token endTagHtml(EndTagToken token) {
+ Token? endTagHtml(EndTagToken token) {
//We repeat the test for the body end tag token being ignored here
if (tree.elementInScope('body')) {
endTagBody(EndTagToken('body'));
@@ -2021,7 +1993,7 @@
}
void endTagListItem(EndTagToken token) {
- String variant;
+ String? variant;
if (token.name == 'li') {
variant = 'list';
} else {
@@ -2056,9 +2028,7 @@
while (!headingElements.contains(node.localName)) {
node = tree.openElements.removeLast();
}
- if (node != null) {
- node.endSourceSpan = token.span;
- }
+ node.endSourceSpan = token.span;
break;
}
}
@@ -2101,7 +2071,7 @@
// Step 2
// Start of the adoption agency algorithm proper
final afeIndex = tree.openElements.indexOf(formattingElement);
- Element furthestBlock;
+ Element? furthestBlock;
for (var element in slice(tree.openElements, afeIndex)) {
if (specialElements.contains(getElementNameTuple(element))) {
furthestBlock = element;
@@ -2114,9 +2084,7 @@
while (element != formattingElement) {
element = tree.openElements.removeLast();
}
- if (element != null) {
- element.endSourceSpan = token.span;
- }
+ element.endSourceSpan = token.span;
tree.activeFormattingElements.remove(element);
return;
}
@@ -2166,7 +2134,7 @@
// Step 6.6
// Remove lastNode from its parents, if any
if (lastNode.parentNode != null) {
- lastNode.parentNode.nodes.remove(lastNode);
+ lastNode.parentNode!.nodes.remove(lastNode);
}
node.nodes.add(lastNode);
// Step 7.7
@@ -2179,13 +2147,13 @@
// table, tbody, tfoot, thead, or tr we need to foster parent the
// lastNode
if (lastNode.parentNode != null) {
- lastNode.parentNode.nodes.remove(lastNode);
+ lastNode.parentNode!.nodes.remove(lastNode);
}
if (const ['table', 'tbody', 'tfoot', 'thead', 'tr']
.contains(commonAncestor.localName)) {
final nodePos = tree.getTableMisnestedNodePosition();
- nodePos[0].insertBefore(lastNode, nodePos[1]);
+ nodePos[0]!.insertBefore(lastNode, nodePos[1]);
} else {
commonAncestor.nodes.add(lastNode);
}
@@ -2262,13 +2230,12 @@
// "Tried to process start tag %s in RCDATA/RAWTEXT mode"%token.name
@override
- // ignore: missing_return
Token processStartTag(StartTagToken token) {
- assert(false);
+ throw StateError('Cannot process start stag in text phase');
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
if (token.name == 'script') {
endTagScript(token);
return null;
@@ -2278,7 +2245,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
tree.insertText(token.data, token.span);
return null;
}
@@ -2289,21 +2256,21 @@
parser.parseError(last.sourceSpan, 'expected-named-closing-tag-but-got-eof',
{'name': last.localName});
tree.openElements.removeLast();
- parser.phase = parser.originalPhase;
+ parser.phase = parser.originalPhase!;
return true;
}
void endTagScript(EndTagToken token) {
final node = tree.openElements.removeLast();
assert(node.localName == 'script');
- parser.phase = parser.originalPhase;
+ parser.phase = parser.originalPhase!;
//The rest of this method is all stuff that only happens if
//document.write works
}
void endTagOther(EndTagToken token) {
tree.openElements.removeLast();
- parser.phase = parser.originalPhase;
+ parser.phase = parser.originalPhase!;
}
}
@@ -2312,7 +2279,7 @@
InTablePhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -2351,7 +2318,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'table':
endTagTable(token);
@@ -2401,7 +2368,7 @@
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
final originalPhase = parser.phase;
parser.phase = parser._inTableTextPhase;
parser._inTableTextPhase.originalPhase = originalPhase;
@@ -2410,7 +2377,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
final originalPhase = parser.phase;
parser.phase = parser._inTableTextPhase;
parser._inTableTextPhase.originalPhase = originalPhase;
@@ -2457,7 +2424,7 @@
return token;
}
- Token startTagTable(StartTagToken token) {
+ Token? startTagTable(StartTagToken token) {
parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag',
{'startName': 'table', 'endName': 'table'});
parser.phase.processEndTag(EndTagToken('table'));
@@ -2467,7 +2434,7 @@
return null;
}
- Token startTagStyleScript(StartTagToken token) {
+ Token? startTagStyleScript(StartTagToken token) {
return parser._inHeadPhase.processStartTag(token);
}
@@ -2536,7 +2503,7 @@
}
class InTableTextPhase extends Phase {
- Phase originalPhase;
+ Phase? originalPhase;
List<StringToken> characterTokens;
InTableTextPhase(HtmlParser parser)
@@ -2548,10 +2515,10 @@
// TODO(sigmund,jmesserly): remove '' (dartbug.com/8480)
final data = characterTokens.map((t) => t.data).join('');
- FileSpan span;
+ FileSpan? span;
if (parser.generateSpans) {
- span = characterTokens[0].span.expand(characterTokens.last.span);
+ span = characterTokens[0].span!.expand(characterTokens.last.span!);
}
if (!allWhitespace(data)) {
@@ -2565,19 +2532,19 @@
@override
Token processComment(CommentToken token) {
flushCharacters();
- parser.phase = originalPhase;
+ parser.phase = originalPhase!;
return token;
}
@override
bool processEOF() {
flushCharacters();
- parser.phase = originalPhase;
+ parser.phase = originalPhase!;
return true;
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
if (token.data == '\u0000') {
return null;
}
@@ -2586,7 +2553,7 @@
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
//pretty sure we should never reach here
characterTokens.add(token);
// XXX assert(false);
@@ -2596,14 +2563,14 @@
@override
Token processStartTag(StartTagToken token) {
flushCharacters();
- parser.phase = originalPhase;
+ parser.phase = originalPhase!;
return token;
}
@override
Token processEndTag(EndTagToken token) {
flushCharacters();
- parser.phase = originalPhase;
+ parser.phase = originalPhase!;
return token;
}
}
@@ -2613,7 +2580,7 @@
InCaptionPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -2633,7 +2600,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'caption':
endTagCaption(token);
@@ -2668,11 +2635,11 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
return parser._inBodyPhase.processCharacters(token);
}
- Token startTagTableElement(StartTagToken token) {
+ Token? startTagTableElement(StartTagToken token) {
parser.parseError(token.span, 'undefined-error');
//XXX Have to duplicate logic here to find out if the tag is ignored
final ignoreEndTag = ignoreEndTagCaption();
@@ -2683,7 +2650,7 @@
return null;
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -2711,7 +2678,7 @@
}
}
- Token endTagTable(EndTagToken token) {
+ Token? endTagTable(EndTagToken token) {
parser.parseError(token.span, 'undefined-error');
final ignoreEndTag = ignoreEndTagCaption();
parser.phase.processEndTag(EndTagToken('caption'));
@@ -2725,7 +2692,7 @@
parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name});
}
- Token endTagOther(EndTagToken token) {
+ Token? endTagOther(EndTagToken token) {
return parser._inBodyPhase.processEndTag(token);
}
}
@@ -2735,7 +2702,7 @@
InColumnGroupPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -2748,7 +2715,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'colgroup':
endTagColgroup(token);
@@ -2778,7 +2745,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
final ignoreEndTag = ignoreEndTagColgroup();
endTagColgroup(EndTagToken('colgroup'));
return ignoreEndTag ? null : token;
@@ -2789,7 +2756,7 @@
tree.openElements.removeLast();
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
final ignoreEndTag = ignoreEndTagColgroup();
endTagColgroup(EndTagToken('colgroup'));
return ignoreEndTag ? null : token;
@@ -2811,7 +2778,7 @@
parser.parseError(token.span, 'no-end-tag', {'name': 'col'});
}
- Token endTagOther(EndTagToken token) {
+ Token? endTagOther(EndTagToken token) {
final ignoreEndTag = ignoreEndTagColgroup();
endTagColgroup(EndTagToken('colgroup'));
return ignoreEndTag ? null : token;
@@ -2823,7 +2790,7 @@
InTableBodyPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -2846,7 +2813,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'tbody':
case 'tfoot':
@@ -2891,12 +2858,12 @@
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return parser._inTablePhase.processSpaceCharacters(token);
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
return parser._inTablePhase.processCharacters(token);
}
@@ -2913,9 +2880,9 @@
return token;
}
- Token startTagTableOther(TagToken token) => endTagTable(token);
+ Token? startTagTableOther(TagToken token) => endTagTable(token);
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
return parser._inTablePhase.processStartTag(token);
}
@@ -2931,7 +2898,7 @@
}
}
- Token endTagTable(TagToken token) {
+ 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') ||
@@ -2952,7 +2919,7 @@
token.span, 'unexpected-end-tag-in-table-body', {'name': token.name});
}
- Token endTagOther(EndTagToken token) {
+ Token? endTagOther(EndTagToken token) {
return parser._inTablePhase.processEndTag(token);
}
}
@@ -2962,7 +2929,7 @@
InRowPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -2984,7 +2951,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'tr':
endTagTr(token);
@@ -3035,12 +3002,12 @@
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return parser._inTablePhase.processSpaceCharacters(token);
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
return parser._inTablePhase.processCharacters(token);
}
@@ -3051,14 +3018,14 @@
tree.activeFormattingElements.add(null);
}
- Token startTagTableOther(StartTagToken token) {
+ Token? startTagTableOther(StartTagToken token) {
final ignoreEndTag = ignoreEndTagTr();
endTagTr(EndTagToken('tr'));
// XXX how are we sure it's always ignored in the innerHTML case?
return ignoreEndTag ? null : token;
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
return parser._inTablePhase.processStartTag(token);
}
@@ -3075,7 +3042,7 @@
}
}
- Token endTagTable(EndTagToken token) {
+ Token? endTagTable(EndTagToken token) {
final ignoreEndTag = ignoreEndTagTr();
endTagTr(EndTagToken('tr'));
// Reprocess the current tag if the tr end tag was not ignored
@@ -3083,7 +3050,7 @@
return ignoreEndTag ? null : token;
}
- Token endTagTableRowGroup(EndTagToken token) {
+ Token? endTagTableRowGroup(EndTagToken token) {
if (tree.elementInScope(token.name, variant: 'table')) {
endTagTr(EndTagToken('tr'));
return token;
@@ -3098,7 +3065,7 @@
token.span, 'unexpected-end-tag-in-table-row', {'name': token.name});
}
- Token endTagOther(EndTagToken token) {
+ Token? endTagOther(EndTagToken token) {
return parser._inTablePhase.processEndTag(token);
}
}
@@ -3108,7 +3075,7 @@
InCellPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -3128,7 +3095,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'td':
case 'th':
@@ -3169,11 +3136,11 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
return parser._inBodyPhase.processCharacters(token);
}
- Token startTagTableOther(StartTagToken token) {
+ Token? startTagTableOther(StartTagToken token) {
if (tree.elementInScope('td', variant: 'table') ||
tree.elementInScope('th', variant: 'table')) {
closeCell();
@@ -3186,7 +3153,7 @@
}
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -3212,7 +3179,7 @@
parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name});
}
- Token endTagImply(EndTagToken token) {
+ Token? endTagImply(EndTagToken token) {
if (tree.elementInScope(token.name, variant: 'table')) {
closeCell();
return token;
@@ -3223,7 +3190,7 @@
return null;
}
- Token endTagOther(EndTagToken token) {
+ Token? endTagOther(EndTagToken token) {
return parser._inBodyPhase.processEndTag(token);
}
}
@@ -3232,7 +3199,7 @@
InSelectPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -3257,7 +3224,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'option':
endTagOption(token);
@@ -3287,7 +3254,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
if (token.data == '\u0000') {
return null;
}
@@ -3318,7 +3285,7 @@
endTagSelect(EndTagToken('select'));
}
- Token startTagInput(StartTagToken token) {
+ Token? startTagInput(StartTagToken token) {
parser.parseError(token.span, 'unexpected-input-in-select');
if (tree.elementInScope('select', variant: 'select')) {
endTagSelect(EndTagToken('select'));
@@ -3329,11 +3296,11 @@
return null;
}
- Token startTagScript(StartTagToken token) {
+ Token? startTagScript(StartTagToken token) {
return parser._inHeadPhase.processStartTag(token);
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
parser.parseError(
token.span, 'unexpected-start-tag-in-select', {'name': token.name});
return null;
@@ -3388,7 +3355,7 @@
InSelectInTablePhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'caption':
case 'table':
@@ -3405,7 +3372,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'caption':
case 'table':
@@ -3428,7 +3395,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
return parser._inSelectPhase.processCharacters(token);
}
@@ -3441,11 +3408,11 @@
return token;
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
return parser._inSelectPhase.processStartTag(token);
}
- Token endTagTable(EndTagToken token) {
+ Token? endTagTable(EndTagToken token) {
parser.parseError(
token.span,
'unexpected-table-element-end-tag-in-select-in-table',
@@ -3457,7 +3424,7 @@
return null;
}
- Token endTagOther(EndTagToken token) {
+ Token? endTagOther(EndTagToken token) {
return parser._inSelectPhase.processEndTag(token);
}
}
@@ -3560,7 +3527,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
if (token.data == '\u0000') {
token.replaceData('\uFFFD');
} else if (parser.framesetOK && !allWhitespace(token.data)) {
@@ -3570,7 +3537,7 @@
}
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
final currentNode = tree.openElements.last;
if (breakoutElements.contains(token.name) ||
(token.name == 'font' &&
@@ -3604,21 +3571,21 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
var nodeIndex = tree.openElements.length - 1;
var node = tree.openElements.last;
if (node.localName?.toAsciiLowerCase() != token.name) {
parser.parseError(token.span, 'unexpected-end-tag', {'name': token.name});
}
- Token newToken;
+ Token? newToken;
while (true) {
if (node.localName?.toAsciiLowerCase() == token.name) {
//XXX this isn't in the spec but it seems necessary
if (parser.phase == parser._inTableTextPhase) {
final inTableText = parser.phase as InTableTextPhase;
inTableText.flushCharacters();
- parser.phase = inTableText.originalPhase;
+ parser.phase = inTableText.originalPhase!;
}
while (tree.openElements.removeLast() != node) {
assert(tree.openElements.isNotEmpty);
@@ -3644,13 +3611,13 @@
AfterBodyPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
if (token.name == 'html') return startTagHtml(token);
return startTagOther(token);
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
if (token.name == 'html') {
endTagHtml(token);
return null;
@@ -3663,7 +3630,7 @@
bool processEOF() => false;
@override
- Token processComment(CommentToken token) {
+ Token? processComment(CommentToken token) {
// This is needed because data is to be appended to the <html> element
// here and not to whatever is currently open.
tree.insertComment(token, tree.openElements[0]);
@@ -3678,7 +3645,7 @@
}
@override
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -3716,7 +3683,7 @@
InFramesetPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -3734,7 +3701,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'frameset':
endTagFrameset(token);
@@ -3757,7 +3724,7 @@
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
parser.parseError(token.span, 'unexpected-char-in-frameset');
return null;
}
@@ -3771,11 +3738,11 @@
tree.openElements.removeLast();
}
- Token startTagNoframes(StartTagToken token) {
+ Token? startTagNoframes(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
- Token startTagOther(StartTagToken token) {
+ Token? startTagOther(StartTagToken token) {
parser.parseError(
token.span, 'unexpected-start-tag-in-frameset', {'name': token.name});
return null;
@@ -3809,7 +3776,7 @@
AfterFramesetPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -3822,7 +3789,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
switch (token.name) {
case 'html':
endTagHtml(token);
@@ -3838,12 +3805,12 @@
bool processEOF() => false;
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
parser.parseError(token.span, 'unexpected-char-after-frameset');
return null;
}
- Token startTagNoframes(StartTagToken token) {
+ Token? startTagNoframes(StartTagToken token) {
return parser._inHeadPhase.processStartTag(token);
}
@@ -3866,7 +3833,7 @@
AfterAfterBodyPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
if (token.name == 'html') return startTagHtml(token);
return startTagOther(token);
}
@@ -3875,13 +3842,13 @@
bool processEOF() => false;
@override
- Token processComment(CommentToken token) {
+ Token? processComment(CommentToken token) {
tree.insertComment(token, tree.document);
return null;
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return parser._inBodyPhase.processSpaceCharacters(token);
}
@@ -3893,7 +3860,7 @@
}
@override
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
@@ -3917,7 +3884,7 @@
AfterAfterFramesetPhase(HtmlParser parser) : super(parser);
@override
- Token processStartTag(StartTagToken token) {
+ Token? processStartTag(StartTagToken token) {
switch (token.name) {
case 'html':
return startTagHtml(token);
@@ -3933,28 +3900,28 @@
bool processEOF() => false;
@override
- Token processComment(CommentToken token) {
+ Token? processComment(CommentToken token) {
tree.insertComment(token, tree.document);
return null;
}
@override
- Token processSpaceCharacters(SpaceCharactersToken token) {
+ Token? processSpaceCharacters(SpaceCharactersToken token) {
return parser._inBodyPhase.processSpaceCharacters(token);
}
@override
- Token processCharacters(CharactersToken token) {
+ Token? processCharacters(CharactersToken token) {
parser.parseError(token.span, 'expected-eof-but-got-char');
return null;
}
@override
- Token startTagHtml(StartTagToken token) {
+ Token? startTagHtml(StartTagToken token) {
return parser._inBodyPhase.processStartTag(token);
}
- Token startTagNoFrames(StartTagToken token) {
+ Token? startTagNoFrames(StartTagToken token) {
return parser._inHeadPhase.processStartTag(token);
}
@@ -3964,7 +3931,7 @@
}
@override
- Token processEndTag(EndTagToken token) {
+ Token? processEndTag(EndTagToken token) {
parser.parseError(
token.span, 'expected-eof-but-got-end-tag', {'name': token.name});
return null;
@@ -3975,31 +3942,31 @@
class ParseError implements SourceSpanException {
final String errorCode;
@override
- final SourceSpan span;
- final Map data;
+ final SourceSpan? span;
+ final Map? data;
ParseError(this.errorCode, this.span, this.data);
- int get line => span.start.line;
+ int get line => span!.start.line;
- int get column => span.start.column;
+ int get column => span!.start.column;
/// Returns the human readable error message for this error.
///
/// Use [SourceSpan.message] or the [toString] from the [span] field to get a
/// message including span information
@override
- String get message => formatStr(errorMessages[errorCode], data);
+ String get message => formatStr(errorMessages[errorCode]!, data);
@override
String toString({color}) {
- final res = span.message(message, color: color);
- return span.sourceUrl == null ? 'ParserError on $res' : 'On $res';
+ final res = span!.message(message, color: color);
+ return span!.sourceUrl == null ? 'ParserError on $res' : 'On $res';
}
}
/// Convenience function to get the pair of namespace and localName.
-Pair<String, String> getElementNameTuple(Element e) {
+Pair<String, String?> getElementNameTuple(Element e) {
final ns = e.namespaceUri ?? Namespaces.html;
return Pair(ns, e.localName);
}
diff --git a/lib/src/constants.dart b/lib/src/constants.dart
index 0f82805..2c013d4 100644
--- a/lib/src/constants.dart
+++ b/lib/src/constants.dart
@@ -4,7 +4,7 @@
// lookup than linear search "contains". In the Python code they were
// frozensets.
-final String eof = null;
+final String? eof = null;
class ReparseException implements Exception {
final String message;
@@ -245,7 +245,7 @@
static const xmlns = 'http://www.w3.org/2000/xmlns/';
Namespaces._();
- static String getPrefix(String url) {
+ static String? getPrefix(String? url) {
switch (url) {
case html:
return 'html';
@@ -405,7 +405,7 @@
const int NEWLINE = 10;
const int RETURN = 13;
-bool isWhitespace(String char) {
+bool isWhitespace(String? char) {
if (char == null) return false;
return isWhitespaceCC(char.codeUnitAt(0));
}
@@ -439,22 +439,22 @@
const UPPER_A = 65;
const UPPER_Z = 90;
-bool isLetterOrDigit(String char) => isLetter(char) || isDigit(char);
+bool isLetterOrDigit(String? char) => isLetter(char) || isDigit(char);
// Note: this is intentially ASCII only
-bool isLetter(String char) {
+bool isLetter(String? char) {
if (char == null) return false;
final cc = char.codeUnitAt(0);
return cc >= LOWER_A && cc <= LOWER_Z || cc >= UPPER_A && cc <= UPPER_Z;
}
-bool isDigit(String char) {
+bool isDigit(String? char) {
if (char == null) return false;
final cc = char.codeUnitAt(0);
return cc >= ZERO && cc < ZERO + 10;
}
-bool isHexDigit(String char) {
+bool isHexDigit(String? char) {
if (char == null) return false;
switch (char.codeUnitAt(0)) {
case 48:
diff --git a/lib/src/css_class_set.dart b/lib/src/css_class_set.dart
index e57c487..cebb565 100644
--- a/lib/src/css_class_set.dart
+++ b/lib/src/css_class_set.dart
@@ -41,7 +41,7 @@
///
/// If [shouldAdd] is true, then we always add that [value] to the element. If
/// [shouldAdd] is false then we always remove [value] from the element.
- bool toggle(String value, [bool shouldAdd]);
+ bool toggle(String value, [bool? shouldAdd]);
/// Returns [:true:] if classes cannot be added or removed from this
/// [:CssClassSet:].
@@ -52,7 +52,7 @@
/// This is the Dart equivalent of jQuery's
/// [hasClass](http://api.jquery.com/hasClass/).
@override
- bool contains(Object value);
+ bool contains(Object? value);
/// Add the class [value] to element.
///
@@ -72,7 +72,7 @@
/// This is the Dart equivalent of jQuery's
/// [removeClass](http://api.jquery.com/removeClass/).
@override
- bool remove(Object value);
+ bool remove(Object? value);
/// Add all classes specified in [iterable] to element.
///
@@ -86,7 +86,7 @@
/// This is the Dart equivalent of jQuery's
/// [removeClass](http://api.jquery.com/removeClass/).
@override
- void removeAll(Iterable<Object> iterable);
+ void removeAll(Iterable<Object?> iterable);
/// Toggles all classes specified in [iterable] on element.
///
@@ -96,7 +96,7 @@
/// If [shouldAdd] is true, then we always add all the classes in [iterable]
/// element. If [shouldAdd] is false then we always remove all the classes in
/// [iterable] from the element.
- void toggleAll(Iterable<String> iterable, [bool shouldAdd]);
+ void toggleAll(Iterable<String> iterable, [bool? shouldAdd]);
}
abstract class _CssClassSetImpl extends SetBase<String> implements CssClassSet {
@@ -111,7 +111,7 @@
/// If [shouldAdd] is true, then we always add that [value] to the element. If
/// [shouldAdd] is false then we always remove [value] from the element.
@override
- bool toggle(String value, [bool shouldAdd]) {
+ bool toggle(String value, [bool? shouldAdd]) {
final s = readClasses();
var result = false;
shouldAdd ??= !s.contains(value);
@@ -142,11 +142,11 @@
/// This is the Dart equivalent of jQuery's
/// [hasClass](http://api.jquery.com/hasClass/).
@override
- bool contains(Object value) => readClasses().contains(value);
+ bool contains(Object? value) => readClasses().contains(value);
/// Lookup from the Set interface. Not interesting for a String set.
@override
- String lookup(Object value) => contains(value) ? value as String : null;
+ String? lookup(Object? value) => contains(value) ? value as String? : null;
@override
Set<String> toSet() => readClasses().toSet();
@@ -168,7 +168,7 @@
/// This is the Dart equivalent of jQuery's
/// [removeClass](http://api.jquery.com/removeClass/).
@override
- bool remove(Object value) {
+ bool remove(Object? value) {
if (value is! String) return false;
final s = readClasses();
final result = s.remove(value);
@@ -185,7 +185,7 @@
/// element. If [shouldAdd] is false then we always remove all the classes in
/// [iterable] from the element.
@override
- void toggleAll(Iterable<String> iterable, [bool shouldAdd]) {
+ void toggleAll(Iterable<String> iterable, [bool? shouldAdd]) {
for (var e in iterable) {
toggle(e, shouldAdd);
}
diff --git a/lib/src/encoding_parser.dart b/lib/src/encoding_parser.dart
index 6fa96c6..e417351 100644
--- a/lib/src/encoding_parser.dart
+++ b/lib/src/encoding_parser.dart
@@ -56,7 +56,7 @@
String get _currentByte => _bytes[_position];
/// Skip past a list of characters. Defaults to skipping [isWhitespace].
- String _skipChars([_CharPredicate skipChars]) {
+ String? _skipChars([_CharPredicate? skipChars]) {
skipChars ??= isWhitespace;
var p = _position; // use property for the error-checking
while (p < _length) {
@@ -71,7 +71,7 @@
return null;
}
- String _skipUntil(_CharPredicate untilChars) {
+ String? _skipUntil(_CharPredicate untilChars) {
var p = _position;
while (p < _length) {
final c = _bytes[p];
@@ -112,7 +112,7 @@
}
}
- String _slice(int start, [int end]) {
+ String _slice(int start, [int? end]) {
end ??= _length;
if (end < 0) end += _length;
return _bytes.substring(start, end);
@@ -131,14 +131,14 @@
/// Mini parser for detecting character encoding from meta elements.
class EncodingParser {
final EncodingBytes _data;
- String _encoding;
+ String? _encoding;
/// [bytes] - the data to work on for encoding detection.
EncodingParser(List<int> bytes)
// Note: this is intentionally interpreting bytes as codepoints.
: _data = EncodingBytes(String.fromCharCodes(bytes).toLowerCase());
- String getEncoding() {
+ String? getEncoding() {
final methodDispatch = [
_DispatchEntry('<!--', _handleComment),
_DispatchEntry('<meta', _handleMeta),
@@ -239,7 +239,7 @@
/// Return a name,value pair for the next attribute in the stream,
/// if one is found, or null
- List<String> _getAttribute() {
+ List<String>? _getAttribute() {
// Step 1 (skip chars)
var c = _data._skipChars((x) => x == '/' || isWhitespace(x));
// Step 2
@@ -312,8 +312,6 @@
c = _data._next();
if (_isSpaceOrAngleBracket(c)) {
return [attrName.join(), attrValue.join()];
- } else if (c == null) {
- return null;
} else if (isLetter(c)) {
attrValue.add(c.toLowerCase());
} else {
@@ -328,7 +326,7 @@
ContentAttrParser(this.data);
- String parse() {
+ String? parse() {
try {
// Check if the attr name is charset
// otherwise return
diff --git a/lib/src/html_input_stream.dart b/lib/src/html_input_stream.dart
index d08163a..60bf8f8 100644
--- a/lib/src/html_input_stream.dart
+++ b/lib/src/html_input_stream.dart
@@ -20,7 +20,7 @@
static const String defaultEncoding = 'utf-8';
/// The name of the character encoding.
- String charEncodingName;
+ String? charEncodingName;
/// True if we are certain about [charEncodingName], false for tenative.
bool charEncodingCertain = true;
@@ -28,20 +28,20 @@
final bool generateSpans;
/// Location where the contents of the stream were found.
- final String sourceUrl;
+ final String? sourceUrl;
- List<int> _rawBytes;
+ List<int>? _rawBytes;
/// Raw UTF-16 codes, used if a Dart String is passed in.
- List<int> _rawChars;
+ List<int>? _rawChars;
- Queue<String> errors;
+ var errors = Queue<String>();
- SourceFile fileInfo;
+ SourceFile? fileInfo;
- List<int> _chars;
+ var _chars = <int>[];
- int _offset;
+ var _offset = 0;
/// Initialise an HtmlInputStream.
///
@@ -58,7 +58,7 @@
///
/// [parseMeta] - Look for a <meta> element containing encoding information
HtmlInputStream(source,
- [String encoding,
+ [String? encoding,
bool parseMeta = true,
this.generateSpans = false,
this.sourceUrl])
@@ -88,18 +88,18 @@
_offset = 0;
_chars = <int>[];
- _rawChars ??= _decodeBytes(charEncodingName, _rawBytes);
+ final rawChars = _rawChars ??= _decodeBytes(charEncodingName!, _rawBytes!);
var skipNewline = false;
var wasSurrogatePair = false;
- for (var i = 0; i < _rawChars.length; i++) {
- var c = _rawChars[i];
+ for (var i = 0; i < rawChars.length; i++) {
+ var c = rawChars[i];
if (skipNewline) {
skipNewline = false;
if (c == NEWLINE) continue;
}
- final isSurrogatePair = _isSurrogatePair(_rawChars, i);
+ final isSurrogatePair = _isSurrogatePair(rawChars, i);
if (!isSurrogatePair && !wasSurrogatePair) {
if (_invalidUnicode(c)) {
errors.add('invalid-codepoint');
@@ -146,12 +146,12 @@
}
// Substitute for equivalent encodings:
- if (charEncodingName.toLowerCase() == 'iso-8859-1') {
+ if (charEncodingName!.toLowerCase() == 'iso-8859-1') {
charEncodingName = 'windows-1252';
}
}
- void changeEncoding(String newEncoding) {
+ void changeEncoding(String? newEncoding) {
if (_rawBytes == null) {
// We should never get here -- if encoding is certain we won't try to
// change it.
@@ -179,17 +179,17 @@
/// Attempts to detect at BOM at the start of the stream. If
/// an encoding can be determined from the BOM return the name of the
/// encoding otherwise return null.
- String detectBOM() {
+ String? detectBOM() {
// Try detecting the BOM using bytes from the string
- if (_hasUtf8Bom(_rawBytes)) {
+ if (_hasUtf8Bom(_rawBytes!)) {
return 'utf-8';
}
return null;
}
/// Report the encoding declared by the meta element.
- String detectEncodingMeta() {
- final parser = EncodingParser(slice(_rawBytes, 0, numBytesMeta));
+ String? detectEncodingMeta() {
+ final parser = EncodingParser(slice(_rawBytes!, 0, numBytesMeta));
var encoding = parser.getEncoding();
if (const ['utf-16', 'utf-16-be', 'utf-16-le'].contains(encoding)) {
@@ -205,14 +205,14 @@
/// Read one character from the stream or queue if available. Return
/// EOF when EOF is reached.
- String char() {
+ String? char() {
if (_offset >= _chars.length) return eof;
return _isSurrogatePair(_chars, _offset)
? String.fromCharCodes([_chars[_offset++], _chars[_offset++]])
: String.fromCharCode(_chars[_offset++]);
}
- String peekChar() {
+ String? peekChar() {
if (_offset >= _chars.length) return eof;
return _isSurrogatePair(_chars, _offset)
? String.fromCharCodes([_chars[_offset], _chars[_offset + 1]])
@@ -236,15 +236,15 @@
/// including any character in 'characters' or EOF.
String charsUntil(String characters, [bool opposite = false]) {
final start = _offset;
- String c;
- while ((c = peekChar()) != null && characters.contains(c) == opposite) {
+ String? c;
+ while ((c = peekChar()) != null && characters.contains(c!) == opposite) {
_offset += c.codeUnits.length;
}
return String.fromCharCodes(_chars.sublist(start, _offset));
}
- void unget(String ch) {
+ void unget(String? ch) {
// Only one character is allowed to be ungotten at once - it must
// be consumed again before any further call to unget
if (ch != null) {
@@ -305,7 +305,7 @@
/// Return the python codec name corresponding to an encoding or null if the
/// string doesn't correspond to a valid encoding.
-String codecName(String encoding) {
+String? codecName(String? encoding) {
final asciiPunctuation = RegExp(
'[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u007E]');
@@ -317,7 +317,7 @@
/// Returns true if the [bytes] starts with a UTF-8 byte order mark.
/// Since UTF-8 doesn't have byte order, it's somewhat of a misnomer, but it is
/// used in HTML to detect the UTF-
-bool _hasUtf8Bom(List<int> bytes, [int offset = 0, int length]) {
+bool _hasUtf8Bom(List<int> bytes, [int offset = 0, int? length]) {
final end = length != null ? offset + length : bytes.length;
return (offset + 3) <= end &&
bytes[offset] == 0xEF &&
diff --git a/lib/src/list_proxy.dart b/lib/src/list_proxy.dart
index ef271ab..61784c7 100644
--- a/lib/src/list_proxy.dart
+++ b/lib/src/list_proxy.dart
@@ -8,7 +8,7 @@
final List<E> _list = <E>[];
@override
- bool remove(Object item) => _list.remove(item);
+ bool remove(Object? item) => _list.remove(item);
@override
int get length => _list.length;
diff --git a/lib/src/query_selector.dart b/lib/src/query_selector.dart
index 9d09857..8880cfe 100644
--- a/lib/src/query_selector.dart
+++ b/lib/src/query_selector.dart
@@ -10,7 +10,7 @@
bool matches(Element node, String selector) =>
SelectorEvaluator().matches(node, _parseSelectorList(selector));
-Element querySelector(Node node, String selector) =>
+Element? querySelector(Node node, String selector) =>
SelectorEvaluator().querySelector(node, _parseSelectorList(selector));
List<Element> querySelectorAll(Node node, String selector) {
@@ -32,14 +32,14 @@
class SelectorEvaluator extends Visitor {
/// The current HTML element to match against.
- Element _element;
+ Element? _element;
bool matches(Element element, SelectorGroup selector) {
_element = element;
return visitSelectorGroup(selector);
}
- Element querySelector(Node root, SelectorGroup selector) {
+ Element? querySelector(Node root, SelectorGroup selector) {
for (var element in root.nodes.whereType<Element>()) {
if (matches(element, selector)) return element;
final result = querySelector(element, selector);
@@ -66,7 +66,7 @@
var result = true;
// Note: evaluate selectors right-to-left as it's more efficient.
- int combinator;
+ int? combinator;
for (var s in selector.simpleSelectorSequences.reversed) {
if (combinator == null) {
result = s.simpleSelector.visit(this) as bool;
@@ -74,7 +74,7 @@
// descendant combinator
// http://dev.w3.org/csswg/selectors-4/#descendant-combinators
do {
- _element = _element.parent;
+ _element = _element!.parent;
} while (_element != null && !(s.simpleSelector.visit(this) as bool));
if (_element == null) result = false;
@@ -82,7 +82,7 @@
// Following-sibling combinator
// http://dev.w3.org/csswg/selectors-4/#general-sibling-combinators
do {
- _element = _element.previousElementSibling;
+ _element = _element!.previousElementSibling;
} while (_element != null && !(s.simpleSelector.visit(this) as bool));
if (_element == null) result = false;
@@ -94,12 +94,12 @@
case TokenKind.COMBINATOR_PLUS:
// Next-sibling combinator
// http://dev.w3.org/csswg/selectors-4/#adjacent-sibling-combinators
- _element = _element.previousElementSibling;
+ _element = _element!.previousElementSibling;
break;
case TokenKind.COMBINATOR_GREATER:
// Child combinator
// http://dev.w3.org/csswg/selectors-4/#child-combinators
- _element = _element.parent;
+ _element = _element!.parent;
break;
case TokenKind.COMBINATOR_DESCENDANT:
case TokenKind.COMBINATOR_TILDE:
@@ -139,34 +139,34 @@
case 'root':
// TODO(jmesserly): fix when we have a .ownerDocument pointer
// return _element == _element.ownerDocument.rootElement;
- return _element.localName == 'html' && _element.parentNode == null;
+ return _element!.localName == 'html' && _element!.parentNode == null;
// http://dev.w3.org/csswg/selectors-4/#the-empty-pseudo
case 'empty':
- return _element.nodes
+ return _element!.nodes
.any((n) => !(n is Element || n is Text && n.text.isNotEmpty));
// http://dev.w3.org/csswg/selectors-4/#the-blank-pseudo
case 'blank':
- return _element.nodes.any((n) => !(n is Element ||
+ return _element!.nodes.any((n) => !(n is Element ||
n is Text && n.text.runes.any((r) => !isWhitespaceCC(r))));
// http://dev.w3.org/csswg/selectors-4/#the-first-child-pseudo
case 'first-child':
- return _element.previousElementSibling == null;
+ return _element!.previousElementSibling == null;
// http://dev.w3.org/csswg/selectors-4/#the-last-child-pseudo
case 'last-child':
- return _element.nextElementSibling == null;
+ return _element!.nextElementSibling == null;
// http://dev.w3.org/csswg/selectors-4/#the-only-child-pseudo
case 'only-child':
- return _element.previousElementSibling == null &&
- _element.nextElementSibling == null;
+ return _element!.previousElementSibling == null &&
+ _element!.nextElementSibling == null;
// http://dev.w3.org/csswg/selectors-4/#link
case 'link':
- return _element.attributes['href'] != null;
+ return _element!.attributes['href'] != null;
case 'visited':
// Always return false since we aren't a browser. This is allowed per:
@@ -215,7 +215,7 @@
final exprs = selector.expression.expressions;
if (exprs.length == 1 && exprs[0] is LiteralTerm) {
final literal = exprs[0] as LiteralTerm;
- final parent = _element.parentNode;
+ final parent = _element!.parentNode;
return parent != null &&
(literal.value as num) > 0 &&
parent.nodes.indexOf(_element) == literal.value;
@@ -234,7 +234,7 @@
throw _unimplemented(selector);
}
- static String _getInheritedLanguage(Node node) {
+ static String? _getInheritedLanguage(Node? node) {
while (node != null) {
final lang = node.attributes['lang'];
if (lang != null) return lang;
@@ -246,37 +246,37 @@
@override
bool visitNamespaceSelector(NamespaceSelector selector) {
// Match element tag name
- if (!(selector.nameAsSimpleSelector.visit(this) as bool)) return false;
+ if (!(selector.nameAsSimpleSelector!.visit(this) as bool)) return false;
if (selector.isNamespaceWildcard) return true;
- if (selector.namespace == '') return _element.namespaceUri == null;
+ if (selector.namespace == '') return _element!.namespaceUri == null;
throw _unimplemented(selector);
}
@override
bool visitElementSelector(ElementSelector selector) =>
- selector.isWildcard || _element.localName == selector.name.toLowerCase();
+ selector.isWildcard || _element!.localName == selector.name.toLowerCase();
@override
- bool visitIdSelector(IdSelector selector) => _element.id == selector.name;
+ bool visitIdSelector(IdSelector selector) => _element!.id == selector.name;
@override
bool visitClassSelector(ClassSelector selector) =>
- _element.classes.contains(selector.name);
+ _element!.classes.contains(selector.name);
// TODO(jmesserly): negation should support any selectors in level 4,
// not just simple selectors.
// http://dev.w3.org/csswg/selectors-4/#negation
@override
bool visitNegationSelector(NegationSelector selector) =>
- !(selector.negationArg.visit(this) as bool);
+ !(selector.negationArg!.visit(this) as bool);
@override
bool visitAttributeSelector(AttributeSelector selector) {
// Match name first
- final value = _element.attributes[selector.name.toLowerCase()];
+ final value = _element!.attributes[selector.name.toLowerCase()];
if (value == null) return false;
if (selector.operatorKind == TokenKind.NO_MATCH) return true;
diff --git a/lib/src/token.dart b/lib/src/token.dart
index 1869b4f..b2c3f40 100644
--- a/lib/src/token.dart
+++ b/lib/src/token.dart
@@ -6,13 +6,13 @@
/// An html5 token.
abstract class Token {
- FileSpan span;
+ FileSpan? span;
int get kind;
}
abstract class TagToken extends Token {
- String name;
+ String? name;
bool selfClosing;
@@ -25,26 +25,27 @@
LinkedHashMap<Object, String> data;
/// The attribute spans if requested. Otherwise null.
- List<TagAttribute> attributeSpans;
+ List<TagAttribute>? attributeSpans;
bool selfClosingAcknowledged;
/// The namespace. This is filled in later during tree building.
- String namespace;
+ String? namespace;
- StartTagToken(String name,
- {this.data,
+ StartTagToken(String? name,
+ {LinkedHashMap<Object, String>? data,
bool selfClosing = false,
this.selfClosingAcknowledged = false,
this.namespace})
- : super(name, selfClosing);
+ : data = data ?? LinkedHashMap(),
+ super(name, selfClosing);
@override
int get kind => TokenKind.startTag;
}
class EndTagToken extends TagToken {
- EndTagToken(String name, {bool selfClosing = false})
+ EndTagToken(String? name, {bool selfClosing = false})
: super(name, selfClosing);
@override
@@ -52,28 +53,28 @@
}
abstract class StringToken extends Token {
- StringBuffer _buffer;
+ StringBuffer? _buffer;
- String _string;
+ String? _string;
String get data {
if (_string == null) {
_string = _buffer.toString();
_buffer = null;
}
- return _string;
+ return _string!;
}
StringToken(this._string) : _buffer = _string == null ? StringBuffer() : null;
StringToken add(String data) {
- _buffer.write(data);
+ _buffer!.write(data);
return this;
}
}
class ParseErrorToken extends StringToken {
/// Extra information that goes along with the error message.
- Map messageParams;
+ Map? messageParams;
ParseErrorToken(String data, {this.messageParams}) : super(data);
@@ -82,7 +83,7 @@
}
class CharactersToken extends StringToken {
- CharactersToken([String data]) : super(data);
+ CharactersToken([String? data]) : super(data);
@override
int get kind => TokenKind.characters;
@@ -96,23 +97,23 @@
}
class SpaceCharactersToken extends StringToken {
- SpaceCharactersToken([String data]) : super(data);
+ SpaceCharactersToken([String? data]) : super(data);
@override
int get kind => TokenKind.spaceCharacters;
}
class CommentToken extends StringToken {
- CommentToken([String data]) : super(data);
+ CommentToken([String? data]) : super(data);
@override
int get kind => TokenKind.comment;
}
class DoctypeToken extends Token {
- String publicId;
- String systemId;
- String name = '';
+ String? publicId;
+ String? systemId;
+ String? name = '';
bool correct;
DoctypeToken({this.publicId, this.systemId, this.correct = false});
@@ -125,15 +126,15 @@
/// They're also used by [StartTagToken.attributeSpans] if attribute spans are
/// requested.
class TagAttribute {
- String name;
- String value;
+ String? name;
+ late String value;
// The spans of the attribute. This is not used unless we are computing an
// attribute span on demand.
- int start;
- int end;
- int startValue;
- int endValue;
+ late int start;
+ late int end;
+ int? startValue;
+ late int endValue;
TagAttribute();
}
diff --git a/lib/src/tokenizer.dart b/lib/src/tokenizer.dart
index 2737a7e..4ee70fc 100644
--- a/lib/src/tokenizer.dart
+++ b/lib/src/tokenizer.dart
@@ -43,33 +43,33 @@
/// This reference to the parser is used for correct CDATA handling.
/// The [HtmlParser] will set this at construction time.
- HtmlParser parser;
+ HtmlParser? parser;
- final Queue<Token> tokenQueue;
+ final Queue<Token?> tokenQueue;
/// Holds the token that is currently being processed.
- Token currentToken;
+ Token? currentToken;
/// Holds a reference to the method to be invoked for the next parser state.
- bool Function() state;
+ late bool Function() state;
final StringBuffer _buffer = StringBuffer();
- int _lastOffset;
+ late int _lastOffset;
// TODO(jmesserly): ideally this would be a LinkedHashMap and we wouldn't add
// an item until it's ready. But the code doesn't have a clear notion of when
// it's "done" with the attribute.
- List<TagAttribute> _attributes;
- Set<String> _attributeNames;
+ List<TagAttribute>? _attributes;
+ Set<String>? _attributeNames;
HtmlTokenizer(doc,
- {String encoding,
+ {String? encoding,
bool parseMeta = true,
this.lowercaseElementName = true,
this.lowercaseAttrName = true,
this.generateSpans = false,
- String sourceUrl,
+ String? sourceUrl,
this.attributeSpans = false})
: stream =
HtmlInputStream(doc, encoding, parseMeta, generateSpans, sourceUrl),
@@ -81,24 +81,24 @@
DoctypeToken get currentDoctypeToken => currentToken as DoctypeToken;
StringToken get currentStringToken => currentToken as StringToken;
- Token _current;
+ Token? _current;
@override
- Token get current => _current;
+ Token get current => _current!;
final StringBuffer _attributeName = StringBuffer();
final StringBuffer _attributeValue = StringBuffer();
void _markAttributeEnd(int offset) {
- _attributes.last.value = '$_attributeValue';
- if (attributeSpans) _attributes.last.end = stream.position + offset;
+ _attributes!.last.value = '$_attributeValue';
+ if (attributeSpans) _attributes!.last.end = stream.position + offset;
}
void _markAttributeValueStart(int offset) {
- if (attributeSpans) _attributes.last.startValue = stream.position + offset;
+ if (attributeSpans) _attributes!.last.startValue = stream.position + offset;
}
void _markAttributeValueEnd(int offset) {
- if (attributeSpans) _attributes.last.endValue = stream.position + offset;
+ if (attributeSpans) _attributes!.last.endValue = stream.position + offset;
_markAttributeEnd(offset);
}
@@ -111,7 +111,7 @@
_attributeName.write(name);
_attributeValue.clear();
final attr = TagAttribute();
- _attributes.add(attr);
+ _attributes!.add(attr);
if (attributeSpans) attr.start = stream.position - name.length;
}
@@ -155,7 +155,7 @@
void _addToken(Token token) {
if (generateSpans && token.span == null) {
final offset = stream.position;
- token.span = stream.fileInfo.span(_lastOffset, offset);
+ token.span = stream.fileInfo!.span(_lastOffset, offset);
if (token is! ParseErrorToken) {
_lastOffset = offset;
}
@@ -255,9 +255,9 @@
return char;
}
- void consumeEntity({String allowedChar, bool fromAttribute = false}) {
+ void consumeEntity({String? allowedChar, bool fromAttribute = false}) {
// Initialise to the default output for when no entity is matched
- var output = '&';
+ String? output = '&';
final charStack = [stream.char()];
if (isWhitespace(charStack[0]) ||
@@ -293,7 +293,7 @@
//
// Consume characters and compare to these to a substring of the
// entity names in the list until the substring no longer matches.
- var filteredEntityList = entitiesByFirstChar[charStack[0]] ?? const [];
+ var filteredEntityList = entitiesByFirstChar[charStack[0]!] ?? const [];
while (charStack.last != eof) {
final name = charStack.join();
@@ -308,7 +308,7 @@
// At this point we have a string that starts with some characters
// that may match an entity
- String entityName;
+ String? entityName;
// Try to find the longest entity the string will match to take care
// of ¬i for instance.
@@ -366,7 +366,7 @@
/// the state to "data" because that's what's needed after a token has been
/// emitted.
void emitCurrentToken() {
- final token = currentToken;
+ final token = currentToken!;
// Add token to the queue to be yielded
if (token is TagToken) {
if (lowercaseElementName) {
@@ -384,8 +384,8 @@
// Convert the list into a map where first key wins.
token.data = LinkedHashMap<Object, String>();
if (_attributes != null) {
- for (var attr in _attributes) {
- token.data.putIfAbsent(attr.name, () => attr.value);
+ for (var attr in _attributes!) {
+ token.data.putIfAbsent(attr.name!, () => attr.value);
}
if (attributeSpans) token.attributeSpans = _attributes;
}
@@ -616,7 +616,7 @@
bool _tokenIsAppropriate() {
// TODO(jmesserly): this should use case insensitive compare instead.
return currentToken is TagToken &&
- currentTagToken.name.toLowerCase() == '$_buffer'.toLowerCase();
+ currentTagToken.name!.toLowerCase() == '$_buffer'.toLowerCase();
}
bool rcdataEndTagNameState() {
@@ -1006,7 +1006,7 @@
final data = stream.char();
if (isWhitespace(data)) {
stream.charsUntil(spaceCharacters, true);
- } else if (isLetter(data)) {
+ } else if (data != null && isLetter(data)) {
_addAttribute(data);
state = attributeNameState;
} else if (data == '>') {
@@ -1016,7 +1016,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('expected-attribute-name-but-got-eof'));
state = dataState;
- } else if ("'\"=<".contains(data)) {
+ } else if ("'\"=<".contains(data!)) {
_addToken(ParseErrorToken('invalid-character-in-attribute-name'));
_addAttribute(data);
state = attributeNameState;
@@ -1057,7 +1057,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-attribute-name'));
state = dataState;
- } else if ("'\"<".contains(data)) {
+ } else if ("'\"<".contains(data!)) {
_addToken(ParseErrorToken('invalid-character-in-attribute-name'));
_attributeName.write(data);
leavingThisState = false;
@@ -1076,12 +1076,12 @@
if (lowercaseAttrName) {
attrName = attrName.toAsciiLowerCase();
}
- _attributes.last.name = attrName;
+ _attributes!.last.name = attrName;
_attributeNames ??= {};
- if (_attributeNames.contains(attrName)) {
+ if (_attributeNames!.contains(attrName)) {
_addToken(ParseErrorToken('duplicate-attribute'));
}
- _attributeNames.add(attrName);
+ _attributeNames!.add(attrName);
// XXX Fix for above XXX
if (emitToken) {
@@ -1099,7 +1099,7 @@
state = beforeAttributeValueState;
} else if (data == '>') {
emitCurrentToken();
- } else if (isLetter(data)) {
+ } else if (data != null && isLetter(data)) {
_addAttribute(data);
state = attributeNameState;
} else if (data == '/') {
@@ -1111,7 +1111,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('expected-end-of-tag-but-got-eof'));
state = dataState;
- } else if ("'\"<".contains(data)) {
+ } else if ("'\"<".contains(data!)) {
_addToken(ParseErrorToken('invalid-character-after-attribute-name'));
_addAttribute(data);
state = attributeNameState;
@@ -1148,7 +1148,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('expected-attribute-value-but-got-eof'));
state = dataState;
- } else if ('=<`'.contains(data)) {
+ } else if ('=<`'.contains(data!)) {
_addToken(ParseErrorToken('equals-in-unquoted-attribute-value'));
_markAttributeValueStart(-1);
_attributeValue.write(data);
@@ -1219,7 +1219,7 @@
_addToken(ParseErrorToken('eof-in-attribute-value-no-quotes'));
_markAttributeValueEnd(-1);
state = dataState;
- } else if ('"\'=<`'.contains(data)) {
+ } else if ('"\'=<`'.contains(data!)) {
_addToken(
ParseErrorToken('unexpected-character-in-unquoted-attribute-value'));
_attributeValue.write(data);
@@ -1299,7 +1299,7 @@
for (var expected in const ['oO', 'cC', 'tT', 'yY', 'pP', 'eE']) {
final char = stream.char();
charStack.add(char);
- if (char == eof || !expected.contains(char)) {
+ if (char == eof || !expected.contains(char!)) {
matched = false;
break;
}
@@ -1311,9 +1311,9 @@
}
} else if (charStack.last == '[' &&
parser != null &&
- parser.tree.openElements.isNotEmpty &&
- parser.tree.openElements.last.namespaceUri !=
- parser.tree.defaultNamespace) {
+ parser!.tree.openElements.isNotEmpty &&
+ parser!.tree.openElements.last.namespaceUri !=
+ parser!.tree.defaultNamespace) {
var matched = true;
for (var expected in const ['C', 'D', 'A', 'T', 'A', '[']) {
charStack.add(stream.char());
@@ -1346,14 +1346,14 @@
currentStringToken.add('\uFFFD');
} else if (data == '>') {
_addToken(ParseErrorToken('incorrect-comment'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-comment'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
- currentStringToken.add(data);
+ currentStringToken.add(data!);
state = commentState;
}
return true;
@@ -1368,14 +1368,14 @@
currentStringToken.add('-\uFFFD');
} else if (data == '>') {
_addToken(ParseErrorToken('incorrect-comment'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-comment'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
- currentStringToken.add('-').add(data);
+ currentStringToken.add('-').add(data!);
state = commentState;
}
return true;
@@ -1390,10 +1390,10 @@
currentStringToken.add('\uFFFD');
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-comment'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
- currentStringToken.add(data).add(stream.charsUntil('-\u0000'));
+ currentStringToken.add(data!).add(stream.charsUntil('-\u0000'));
}
return true;
}
@@ -1408,10 +1408,10 @@
state = commentState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-comment-end-dash'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
- currentStringToken.add('-').add(data);
+ currentStringToken.add('-').add(data!);
state = commentState;
}
return true;
@@ -1420,7 +1420,7 @@
bool commentEndState() {
final data = stream.char();
if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == '\u0000') {
_addToken(ParseErrorToken('invalid-codepoint'));
@@ -1433,15 +1433,15 @@
} else if (data == '-') {
_addToken(
ParseErrorToken('unexpected-dash-after-double-dash-in-comment'));
- currentStringToken.add(data);
+ currentStringToken.add(data!);
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-comment-double-dash'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
// XXX
_addToken(ParseErrorToken('unexpected-char-in-comment'));
- currentStringToken.add('--').add(data);
+ currentStringToken.add('--').add(data!);
state = commentState;
}
return true;
@@ -1450,7 +1450,7 @@
bool commentEndBangState() {
final data = stream.char();
if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == '-') {
currentStringToken.add('--!');
@@ -1461,10 +1461,10 @@
state = commentState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-comment-end-bang-state'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
- currentStringToken.add('--!').add(data);
+ currentStringToken.add('--!').add(data!);
state = commentState;
}
return true;
@@ -1477,7 +1477,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('expected-doctype-name-but-got-eof'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
_addToken(ParseErrorToken('need-space-after-doctype'));
@@ -1494,7 +1494,7 @@
} else if (data == '>') {
_addToken(ParseErrorToken('expected-doctype-name-but-got-right-bracket'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == '\u0000') {
_addToken(ParseErrorToken('invalid-codepoint'));
@@ -1503,7 +1503,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('expected-doctype-name-but-got-eof'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
currentDoctypeToken.name = data;
@@ -1519,7 +1519,7 @@
state = afterDoctypeNameState;
} else if (data == '>') {
currentDoctypeToken.name = currentDoctypeToken.name?.toAsciiLowerCase();
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == '\u0000') {
_addToken(ParseErrorToken('invalid-codepoint'));
@@ -1529,7 +1529,7 @@
_addToken(ParseErrorToken('eof-in-doctype-name'));
currentDoctypeToken.correct = false;
currentDoctypeToken.name = currentDoctypeToken.name?.toAsciiLowerCase();
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
currentDoctypeToken.name = '${currentDoctypeToken.name}$data';
@@ -1542,13 +1542,13 @@
if (isWhitespace(data)) {
return true;
} else if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
currentDoctypeToken.correct = false;
stream.unget(data);
_addToken(ParseErrorToken('eof-in-doctype'));
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
if (data == 'p' || data == 'P') {
@@ -1556,7 +1556,7 @@
var matched = true;
for (var expected in const ['uU', 'bB', 'lL', 'iI', 'cC']) {
data = stream.char();
- if (data == eof || !expected.contains(data)) {
+ if (data == eof || !expected.contains(data!)) {
matched = false;
break;
}
@@ -1569,7 +1569,7 @@
var matched = true;
for (var expected in const ['yY', 'sS', 'tT', 'eE', 'mM']) {
data = stream.char();
- if (data == eof || !expected.contains(data)) {
+ if (data == eof || !expected.contains(data!)) {
matched = false;
break;
}
@@ -1604,7 +1604,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
stream.unget(data);
@@ -1626,12 +1626,12 @@
} else if (data == '>') {
_addToken(ParseErrorToken('unexpected-end-of-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
@@ -1651,12 +1651,12 @@
} else if (data == '>') {
_addToken(ParseErrorToken('unexpected-end-of-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
currentDoctypeToken.publicId = '${currentDoctypeToken.publicId}$data';
@@ -1674,12 +1674,12 @@
} else if (data == '>') {
_addToken(ParseErrorToken('unexpected-end-of-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
currentDoctypeToken.publicId = '${currentDoctypeToken.publicId}$data';
@@ -1692,7 +1692,7 @@
if (isWhitespace(data)) {
state = betweenDoctypePublicAndSystemIdentifiersState;
} else if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == '"') {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
@@ -1705,7 +1705,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
@@ -1720,7 +1720,7 @@
if (isWhitespace(data)) {
return true;
} else if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == '"') {
currentDoctypeToken.systemId = '';
@@ -1731,7 +1731,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
@@ -1752,7 +1752,7 @@
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
stream.unget(data);
@@ -1774,12 +1774,12 @@
} else if (data == '>') {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
@@ -1799,12 +1799,12 @@
} else if (data == '>') {
_addToken(ParseErrorToken('unexpected-end-of-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
currentDoctypeToken.systemId = '${currentDoctypeToken.systemId}$data';
@@ -1822,12 +1822,12 @@
} else if (data == '>') {
_addToken(ParseErrorToken('unexpected-end-of-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
currentDoctypeToken.systemId = '${currentDoctypeToken.systemId}$data';
@@ -1840,12 +1840,12 @@
if (isWhitespace(data)) {
return true;
} else if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
_addToken(ParseErrorToken('eof-in-doctype'));
currentDoctypeToken.correct = false;
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else {
_addToken(ParseErrorToken('unexpected-char-in-doctype'));
@@ -1857,12 +1857,12 @@
bool bogusDoctypeState() {
final data = stream.char();
if (data == '>') {
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
} else if (data == eof) {
// XXX EMIT
stream.unget(data);
- _addToken(currentToken);
+ _addToken(currentToken!);
state = dataState;
}
return true;
@@ -1873,7 +1873,7 @@
var matchedEnd = 0;
while (true) {
var ch = stream.char();
- if (ch == eof) {
+ if (ch == null) {
break;
}
// Deal with null here rather than in the parser
diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart
index 3687464..04c3a19 100644
--- a/lib/src/treebuilder.dart
+++ b/lib/src/treebuilder.dart
@@ -16,7 +16,7 @@
/// entering some elements.
///
/// https://html.spec.whatwg.org/multipage/parsing.html#list-of-active-formatting-elements
-class ActiveFormattingElements extends ListProxy<Element /*?*/ > {
+class ActiveFormattingElements extends ListProxy<Element?> {
/// Push an element into the active formatting elements.
///
/// Prevents equivalent elements from appearing more than 3 times following
@@ -25,7 +25,7 @@
// TODO - Earliest equivalent following a marker, as opposed to earliest
// identical regardless of marker position, should be removed.
@override
- void add(Element node) {
+ void add(Element? node) {
var equalCount = 0;
if (node != null) {
for (var element in reversed) {
@@ -71,21 +71,21 @@
/// Basic treebuilder implementation.
class TreeBuilder {
- final String defaultNamespace;
+ final String? defaultNamespace;
- Document document;
+ var document = Document();
final List<Element> openElements = <Element>[];
final activeFormattingElements = ActiveFormattingElements();
- Node headPointer;
+ Node? headPointer;
- Element formPointer;
+ Element? formPointer;
/// Switch the function used to insert an element from the
/// normal one to the misnested table one and back again
- bool insertFromTable;
+ var insertFromTable = false;
TreeBuilder(bool namespaceHTMLElements)
: defaultNamespace = namespaceHTMLElements ? Namespaces.html : null {
@@ -105,7 +105,7 @@
document = Document();
}
- bool elementInScope(target, {String variant}) {
+ bool elementInScope(target, {String? variant}) {
//If we pass a node in we match that. if we pass a string
//match any node with that name
final exactNode = target is Node;
@@ -193,7 +193,7 @@
entry = activeFormattingElements[i];
// TODO(jmesserly): optimize this. No need to create a token.
- final cloneToken = StartTagToken(entry.localName,
+ final cloneToken = StartTagToken(entry!.localName,
namespace: entry.namespaceUri,
data: LinkedHashMap.from(entry.attributes))
..span = entry.sourceSpan;
@@ -221,7 +221,7 @@
/// Check if an element exists between the end of the active
/// formatting elements and the last marker. If it does, return it, else
/// return null.
- Element elementInActiveFormattingElements(String name) {
+ Element? elementInActiveFormattingElements(String? name) {
for (var item in activeFormattingElements.reversed) {
// Check for Marker first because if it's a Marker it doesn't have a
// name attribute.
@@ -246,7 +246,7 @@
document.nodes.add(doctype);
}
- void insertComment(StringToken token, [Node parent]) {
+ void insertComment(StringToken token, [Node? parent]) {
parent ??= openElements.last;
parent.nodes.add(Comment(token.data)..sourceSpan = token.span);
}
@@ -290,9 +290,9 @@
// TODO(jmesserly): I don't think this is reachable. If insertFromTable
// is true, there will be a <table> element open, and it always has a
// parent pointer.
- nodePos[0].nodes.add(element);
+ nodePos[0]!.nodes.add(element);
} else {
- nodePos[0].insertBefore(element, nodePos[1]);
+ nodePos[0]!.insertBefore(element, nodePos[1]);
}
openElements.add(element);
}
@@ -300,7 +300,7 @@
}
/// Insert text data.
- void insertText(String data, FileSpan span) {
+ void insertText(String data, FileSpan? span) {
final parent = openElements.last;
if (!insertFromTable ||
@@ -311,14 +311,14 @@
// We should be in the InTable mode. This means we want to do
// special magic element rearranging
final nodePos = getTableMisnestedNodePosition();
- _insertText(nodePos[0], data, span, nodePos[1] as Element);
+ _insertText(nodePos[0]!, data, span, nodePos[1] as Element?);
}
}
/// Insert [data] as text in the current node, positioned before the
/// start of node [refNode] or to the end of the node's text.
- static void _insertText(Node parent, String data, FileSpan span,
- [Element refNode]) {
+ static void _insertText(Node parent, String data, FileSpan? span,
+ [Element? refNode]) {
final nodes = parent.nodes;
if (refNode == null) {
if (nodes.isNotEmpty && nodes.last is Text) {
@@ -327,7 +327,7 @@
if (span != null) {
last.sourceSpan =
- span.file.span(last.sourceSpan.start.offset, span.end.offset);
+ span.file.span(last.sourceSpan!.start.offset, span.end.offset);
}
} else {
nodes.add(Text(data)..sourceSpan = span);
@@ -345,13 +345,13 @@
/// Get the foster parent element, and sibling to insert before
/// (or null) when inserting a misnested table node
- List<Node> getTableMisnestedNodePosition() {
+ List<Node?> getTableMisnestedNodePosition() {
// The foster parent element is the one which comes before the most
// recently opened table element
// XXX - this is really inelegant
- Element lastTable;
- Node fosterParent;
- Node insertBefore;
+ Element? lastTable;
+ Node? fosterParent;
+ Node? insertBefore;
for (var elm in openElements.reversed) {
if (elm.localName == 'table') {
lastTable = elm;
@@ -373,7 +373,7 @@
return [fosterParent, insertBefore];
}
- void generateImpliedEndTags([String exclude]) {
+ void generateImpliedEndTags([String? exclude]) {
final name = openElements.last.localName;
// XXX td, th and tr are not actually needed
if (name != exclude &&
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 559f6c3..001c706 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -10,14 +10,15 @@
int get hashCode => 37 * first.hashCode + second.hashCode;
@override
- bool operator ==(other) => other.first == first && other.second == second;
+ bool operator ==(Object other) =>
+ other is Pair && other.first == first && other.second == second;
}
bool startsWithAny(String str, List<String> prefixes) =>
prefixes.any(str.startsWith);
// Like the python [:] operator.
-List<T> slice<T>(List<T> list, int start, [int end]) {
+List<T> slice<T>(List<T> list, int start, [int? end]) {
end ??= list.length;
if (end < 0) end += list.length;
@@ -50,7 +51,7 @@
/// Format a string like Python's % string format operator. Right now this only
/// supports a [data] dictionary used with %s or %08x. Those were the only
/// things needed for [errorMessages].
-String formatStr(String format, Map data) {
+String formatStr(String format, Map? data) {
if (data == null) return format;
data.forEach((key, value) {
final result = StringBuffer();
@@ -64,7 +65,7 @@
while (isDigit(format[digits])) {
digits++;
}
- int numberSize;
+ var numberSize = 0;
if (digits > match) {
numberSize = int.parse(format.substring(match, digits));
match = digits;
diff --git a/pubspec.yaml b/pubspec.yaml
index a8fcab3..5dc06eb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,17 +1,23 @@
name: html
-version: 0.15.0-dev
+version: 0.15.0-nullsafety-dev
description: APIs for parsing and manipulating HTML content outside the browser.
homepage: https://github.com/dart-lang/html
+publish_to: none
+
environment:
- sdk: '>=2.8.0 <3.0.0'
+ sdk: ">=2.12.0-0 <3.0.0"
dependencies:
- csslib: '>=0.13.2 <0.17.0'
- source_span: '>=1.0.0 <2.0.0'
+ csslib: ^0.17.0
+ source_span: ^1.8.0-nullsafety
dev_dependencies:
- path: ^1.6.2
- pedantic: ^1.3.0
- test: ^1.3.0
+ path: ^1.8.0-nullsafety
+ pedantic: ^1.10.0-nullsafety
+ test: ^1.16.0-nullsafety
+
+dependency_overrides:
+ csslib:
+ git: git://github.com/dart-lang/csslib.git
diff --git a/test/parser_feature_test.dart b/test/parser_feature_test.dart
index 0237172..7825a23 100644
--- a/test/parser_feature_test.dart
+++ b/test/parser_feature_test.dart
@@ -49,11 +49,11 @@
expect(error.errorCode, 'unexpected-doctype');
// Note: these values are 0-based, but the printed format is 1-based.
- expect(error.span.start.line, 3);
- expect(error.span.end.line, 3);
- expect(error.span.start.column, 2);
- expect(error.span.end.column, 17);
- expect(error.span.text, '<!DOCTYPE html>');
+ expect(error.span!.start.line, 3);
+ expect(error.span!.end.line, 3);
+ expect(error.span!.start.column, 2);
+ expect(error.span!.end.column, 17);
+ expect(error.span!.text, '<!DOCTYPE html>');
expect(error.toString(), '''
On line 4, column 3 of ParseError: Unexpected DOCTYPE. Ignored.
@@ -77,9 +77,9 @@
expect(parser.errors.length, 1);
final error = parser.errors[0];
expect(error.errorCode, 'unexpected-doctype');
- expect(error.span.start.line, 3);
+ expect(error.span!.start.line, 3);
// Note: error position is at the end, not the beginning
- expect(error.span.start.column, 17);
+ expect(error.span!.start.column, 17);
});
test('text spans should have the correct length', () {
@@ -89,21 +89,21 @@
final text = doc.body.nodes[0].nodes[0] as Text;
expect(text, const TypeMatcher<Text>());
expect(text.data, textContent);
- expect(text.sourceSpan.start.offset, html.indexOf(textContent));
- expect(text.sourceSpan.length, textContent.length);
+ expect(text.sourceSpan!.start.offset, html.indexOf(textContent));
+ expect(text.sourceSpan!.length, textContent.length);
});
test('attribute spans', () {
final text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('element');
- expect(elem.sourceSpan.start.offset, 0);
- expect(elem.sourceSpan.end.offset, text.length);
- expect(elem.sourceSpan.text, text);
+ final elem = doc.querySelector('element')!;
+ expect(elem.sourceSpan!.start.offset, 0);
+ expect(elem.sourceSpan!.end.offset, text.length);
+ expect(elem.sourceSpan!.text, text);
- expect(elem.attributeSpans['quux'], null);
+ expect(elem.attributeSpans!['quux'], null);
- final span = elem.attributeSpans['extends'];
+ final span = elem.attributeSpans!['extends']!;
expect(span.start.offset, text.indexOf('extends'));
expect(span.text, 'extends="x-bar"');
});
@@ -111,11 +111,11 @@
test('attribute value spans', () {
final text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('element');
+ final elem = doc.querySelector('element')!;
- expect(elem.attributeValueSpans['quux'], null);
+ expect(elem.attributeValueSpans!['quux'], null);
- final span = elem.attributeValueSpans['extends'];
+ final span = elem.attributeValueSpans!['extends']!;
expect(span.start.offset, text.indexOf('x-bar'));
expect(span.text, 'x-bar');
});
@@ -123,29 +123,29 @@
test('attribute spans if no attributes', () {
final text = '<element>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('element');
+ final elem = doc.querySelector('element')!;
- expect(elem.attributeSpans['quux'], null);
- expect(elem.attributeValueSpans['quux'], null);
+ expect(elem.attributeSpans!['quux'], null);
+ expect(elem.attributeValueSpans!['quux'], null);
});
test('attribute spans if no attribute value', () {
final text = '<foo template>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('foo');
+ final elem = doc.querySelector('foo')!;
- expect(
- elem.attributeSpans['template'].start.offset, text.indexOf('template'));
- expect(elem.attributeValueSpans.containsKey('template'), false);
+ expect(elem.attributeSpans!['template']!.start.offset,
+ text.indexOf('template'));
+ expect(elem.attributeValueSpans!.containsKey('template'), false);
});
test('attribute spans null if code parsed without spans', () {
final text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
final doc = parse(text);
- final elem = doc.querySelector('element');
+ final elem = doc.querySelector('element')!;
expect(elem.sourceSpan, null);
- expect(elem.attributeSpans['quux'], null);
- expect(elem.attributeSpans['extends'], null);
+ expect(elem.attributeSpans!['quux'], null);
+ expect(elem.attributeSpans!['extends'], null);
});
test('void element innerHTML', () {
@@ -179,9 +179,9 @@
// However, we preserve the input order via LinkedHashMap
final body = parse('<foo d=1 a=2 c=3 b=4>').body;
expect(body.innerHtml, '<foo d="1" a="2" c="3" b="4"></foo>');
- expect(body.querySelector('foo').attributes.remove('a'), '2');
+ expect(body.querySelector('foo')!.attributes.remove('a'), '2');
expect(body.innerHtml, '<foo d="1" c="3" b="4"></foo>');
- body.querySelector('foo').attributes['a'] = '0';
+ body.querySelector('foo')!.attributes['a'] = '0';
expect(body.innerHtml, '<foo d="1" c="3" b="4" a="0"></foo>');
});
@@ -229,7 +229,7 @@
xlink:href="http://example.com/logo.png"
xlink:show="new"></desc>
''');
- final n = doc.querySelector('desc');
+ final n = doc.querySelector('desc')!;
final keys = n.attributes.keys.toList();
expect(
keys.first,
@@ -266,8 +266,8 @@
test('Element.text', () {
final doc = parseFragment('<div>foo<div>bar</div>baz</div>');
- final e = doc.firstChild;
- final text = e.firstChild;
+ final e = doc.firstChild!;
+ final text = e.firstChild!;
expect((text as Text).data, 'foo');
expect(e.text, 'foobarbaz');
@@ -280,7 +280,7 @@
test('Text.text', () {
final doc = parseFragment('<div>foo<div>bar</div>baz</div>');
- final e = doc.firstChild;
+ final e = doc.firstChild!;
final text = e.firstChild as Text;
expect(text.data, 'foo');
expect(text.text, 'foo');
@@ -293,14 +293,14 @@
test('Comment.text', () {
final doc = parseFragment('<div><!--foo-->bar</div>');
- final e = doc.firstChild;
- final c = e.firstChild;
+ final e = doc.firstChild!;
+ final c = e.firstChild!;
expect((c as Comment).data, 'foo');
expect(c.text, 'foo');
expect(e.text, 'bar');
c.text = 'qux';
- expect((c as Comment).data, 'qux');
+ expect(c.data, 'qux');
expect(c.text, 'qux');
expect(e.text, 'bar');
});
@@ -315,12 +315,12 @@
</svg>''');
final doc = p.parseFragment();
expect(p.errors, isEmpty);
- final svg = doc.querySelector('svg');
+ final svg = doc.querySelector('svg')!;
expect(svg.children[0].children[0].localName, 'x-flow');
});
group('Encoding pre-parser', () {
- String getEncoding(String s) => EncodingParser(s.codeUnits).getEncoding();
+ String? getEncoding(String s) => EncodingParser(s.codeUnits).getEncoding();
test('gets encoding from meta charset', () {
expect(getEncoding('<meta charset="utf-16">'), 'utf-16');
@@ -365,88 +365,88 @@
final text = '<html><body>123</body></html>';
final doc = parse(text, generateSpans: true);
{
- final elem = doc.querySelector('html');
- assertSpan(elem.sourceSpan, 0, 6, '<html>');
- assertSpan(elem.endSourceSpan, 22, 29, '</html>');
+ final elem = doc.querySelector('html')!;
+ assertSpan(elem.sourceSpan!, 0, 6, '<html>');
+ assertSpan(elem.endSourceSpan!, 22, 29, '</html>');
}
{
- final elem = doc.querySelector('body');
- assertSpan(elem.sourceSpan, 6, 12, '<body>');
- assertSpan(elem.endSourceSpan, 15, 22, '</body>');
+ final elem = doc.querySelector('body')!;
+ assertSpan(elem.sourceSpan!, 6, 12, '<body>');
+ assertSpan(elem.endSourceSpan!, 15, 22, '</body>');
}
});
test('normal', () {
final text = '<div><element><span></span></element></div>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('element');
- assertSpan(elem.sourceSpan, 5, 14, '<element>');
- assertSpan(elem.endSourceSpan, 27, 37, '</element>');
+ final elem = doc.querySelector('element')!;
+ assertSpan(elem.sourceSpan!, 5, 14, '<element>');
+ assertSpan(elem.endSourceSpan!, 27, 37, '</element>');
});
test('block', () {
final text = '<div>123</div>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('div');
- assertSpan(elem.sourceSpan, 0, 5, '<div>');
- assertSpan(elem.endSourceSpan, 8, 14, '</div>');
+ final elem = doc.querySelector('div')!;
+ assertSpan(elem.sourceSpan!, 0, 5, '<div>');
+ assertSpan(elem.endSourceSpan!, 8, 14, '</div>');
});
test('form', () {
final text = '<form>123</form>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('form');
- assertSpan(elem.sourceSpan, 0, 6, '<form>');
- assertSpan(elem.endSourceSpan, 9, 16, '</form>');
+ final elem = doc.querySelector('form')!;
+ assertSpan(elem.sourceSpan!, 0, 6, '<form>');
+ assertSpan(elem.endSourceSpan!, 9, 16, '</form>');
});
test('p explicit end', () {
final text = '<p>123</p>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('p');
- assertSpan(elem.sourceSpan, 0, 3, '<p>');
- assertSpan(elem.endSourceSpan, 6, 10, '</p>');
+ final elem = doc.querySelector('p')!;
+ assertSpan(elem.sourceSpan!, 0, 3, '<p>');
+ assertSpan(elem.endSourceSpan!, 6, 10, '</p>');
});
test('p implicit end', () {
final text = '<div><p>123<p>456</div>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('p');
- assertSpan(elem.sourceSpan, 5, 8, '<p>');
+ final elem = doc.querySelector('p')!;
+ assertSpan(elem.sourceSpan!, 5, 8, '<p>');
expect(elem.endSourceSpan, isNull);
});
test('li', () {
final text = '<li>123</li>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('li');
- assertSpan(elem.sourceSpan, 0, 4, '<li>');
- assertSpan(elem.endSourceSpan, 7, 12, '</li>');
+ final elem = doc.querySelector('li')!;
+ assertSpan(elem.sourceSpan!, 0, 4, '<li>');
+ assertSpan(elem.endSourceSpan!, 7, 12, '</li>');
});
test('heading', () {
final text = '<h1>123</h1>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('h1');
- assertSpan(elem.sourceSpan, 0, 4, '<h1>');
- assertSpan(elem.endSourceSpan, 7, 12, '</h1>');
+ final elem = doc.querySelector('h1')!;
+ assertSpan(elem.sourceSpan!, 0, 4, '<h1>');
+ assertSpan(elem.endSourceSpan!, 7, 12, '</h1>');
});
test('formatting', () {
final text = '<b>123</b>';
final doc = parse(text, generateSpans: true);
- final elem = doc.querySelector('b');
- assertSpan(elem.sourceSpan, 0, 3, '<b>');
- assertSpan(elem.endSourceSpan, 6, 10, '</b>');
+ final elem = doc.querySelector('b')!;
+ assertSpan(elem.sourceSpan!, 0, 3, '<b>');
+ assertSpan(elem.endSourceSpan!, 6, 10, '</b>');
});
test('table tbody', () {
final text = '<table><tbody> </tbody></table>';
final doc = parse(text, generateSpans: true);
{
- final elem = doc.querySelector('tbody');
- assertSpan(elem.sourceSpan, 7, 14, '<tbody>');
- assertSpan(elem.endSourceSpan, 16, 24, '</tbody>');
+ final elem = doc.querySelector('tbody')!;
+ assertSpan(elem.sourceSpan!, 7, 14, '<tbody>');
+ assertSpan(elem.endSourceSpan!, 16, 24, '</tbody>');
}
});
@@ -454,19 +454,19 @@
final text = '<table><tr><td>123</td></tr></table>';
final doc = parse(text, generateSpans: true);
{
- final elem = doc.querySelector('table');
- assertSpan(elem.sourceSpan, 0, 7, '<table>');
- assertSpan(elem.endSourceSpan, 28, 36, '</table>');
+ final elem = doc.querySelector('table')!;
+ assertSpan(elem.sourceSpan!, 0, 7, '<table>');
+ assertSpan(elem.endSourceSpan!, 28, 36, '</table>');
}
{
- final elem = doc.querySelector('tr');
- assertSpan(elem.sourceSpan, 7, 11, '<tr>');
- assertSpan(elem.endSourceSpan, 23, 28, '</tr>');
+ final elem = doc.querySelector('tr')!;
+ assertSpan(elem.sourceSpan!, 7, 11, '<tr>');
+ assertSpan(elem.endSourceSpan!, 23, 28, '</tr>');
}
{
- final elem = doc.querySelector('td');
- assertSpan(elem.sourceSpan, 11, 15, '<td>');
- assertSpan(elem.endSourceSpan, 18, 23, '</td>');
+ final elem = doc.querySelector('td')!;
+ assertSpan(elem.sourceSpan!, 11, 15, '<td>');
+ assertSpan(elem.endSourceSpan!, 18, 23, '</td>');
}
});
@@ -474,19 +474,19 @@
final text = '<select><optgroup><option>123</option></optgroup></select>';
final doc = parse(text, generateSpans: true);
{
- final elem = doc.querySelector('select');
- assertSpan(elem.sourceSpan, 0, 8, '<select>');
- assertSpan(elem.endSourceSpan, 49, 58, '</select>');
+ final elem = doc.querySelector('select')!;
+ assertSpan(elem.sourceSpan!, 0, 8, '<select>');
+ assertSpan(elem.endSourceSpan!, 49, 58, '</select>');
}
{
- final elem = doc.querySelector('optgroup');
- assertSpan(elem.sourceSpan, 8, 18, '<optgroup>');
- assertSpan(elem.endSourceSpan, 38, 49, '</optgroup>');
+ final elem = doc.querySelector('optgroup')!;
+ assertSpan(elem.sourceSpan!, 8, 18, '<optgroup>');
+ assertSpan(elem.endSourceSpan!, 38, 49, '</optgroup>');
}
{
- final elem = doc.querySelector('option');
- assertSpan(elem.sourceSpan, 18, 26, '<option>');
- assertSpan(elem.endSourceSpan, 29, 38, '</option>');
+ final elem = doc.querySelector('option')!;
+ assertSpan(elem.sourceSpan!, 18, 26, '<option>');
+ assertSpan(elem.endSourceSpan!, 29, 38, '</option>');
}
});
});
diff --git a/test/parser_test.dart b/test/parser_test.dart
index 7bdbe23..06fb4c6 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -32,10 +32,10 @@
void runParserTest(
String groupName,
- String innerHTML,
- String input,
- String expected,
- List errors,
+ String? innerHTML,
+ String? input,
+ String? expected,
+ List? errors,
TreeBuilderFactory treeCtor,
bool namespaceHTMLElements) {
// XXX - move this out into the setup function
@@ -53,7 +53,7 @@
final output = testSerializer(document);
if (namespaceHTMLElements) {
- expected = namespaceHtml(expected);
+ expected = namespaceHtml(expected!);
}
expect(output, equals(expected),
@@ -61,7 +61,7 @@
'\n\nInput:\n$input\n\nExpected:\n$expected\n\nReceived:\n$output');
if (checkParseErrors) {
- expect(parser.errors.length, equals(errors.length),
+ expect(parser.errors.length, equals(errors!.length),
reason: '\n\nInput:\n$input\n\nExpected errors (${errors.length}):\n'
"${errors.join('\n')}\n\n"
'Actual errors (${parser.errors.length}):\n'
@@ -84,9 +84,9 @@
final innerHTML = testData['document-fragment'];
final expected = testData['document'];
- for (var treeCtor in treeTypes.values) {
+ for (var treeCtor in treeTypes!.values) {
for (var namespaceHTMLElements in const [false, true]) {
- test(_nameFor(input), () {
+ test(_nameFor(input!), () {
runParserTest(testName, innerHTML, input, expected, errors,
treeCtor, namespaceHTMLElements);
});
diff --git a/test/selectors/level1_baseline_test.dart b/test/selectors/level1_baseline_test.dart
index 19d1123..cba4c21 100644
--- a/test/selectors/level1_baseline_test.dart
+++ b/test/selectors/level1_baseline_test.dart
@@ -65,7 +65,7 @@
//doc = frame.contentDocument; // Document Node tests
doc = await testContentDocument();
- final element = doc.getElementById('root'); // In-document Element Node tests
+ final element = doc.getElementById('root')!; // In-document Element Node tests
//Setup the namespace tests
setupSpecialElements(element);
diff --git a/test/selectors/level1_lib.dart b/test/selectors/level1_lib.dart
index e22f9c4..65f1468 100644
--- a/test/selectors/level1_lib.dart
+++ b/test/selectors/level1_lib.dart
@@ -12,7 +12,7 @@
import 'package:html/dom.dart';
import 'package:test/test.dart' as unittest;
-Document doc;
+late Document doc;
/*
* Create and append special elements that cannot be created correctly with HTML markup alone.
@@ -96,7 +96,7 @@
void verifyStaticList(String type, dynamic root) {
List pre;
List post;
- int preLength;
+ late int preLength;
runTest(() {
pre = root.querySelectorAll('div') as List;
@@ -184,7 +184,7 @@
/// Tests containing this string fail for an unknown reason
final _failureName = 'matching custom data-* attribute with';
-String _getSkip(String name) {
+String? _getSkip(String name) {
if (name.contains(_failureName)) {
return 'Tests related to `$_failureName` fail for an unknown reason.';
}
@@ -195,10 +195,10 @@
* Execute queries with the specified valid selectors for both querySelector() and querySelectorAll()
* Only run these tests when results are expected. Don't run for syntax error tests.
*/
-void runValidSelectorTest(String type, root,
+void runValidSelectorTest(String type, Node root,
List<Map<String, dynamic>> selectors, testType, docType) {
var nodeType = '';
- switch ((root as Node).nodeType) {
+ switch (root.nodeType) {
case Node.DOCUMENT_NODE:
nodeType = 'document';
break;
@@ -217,20 +217,20 @@
final n = s['name'] as String;
final skip = _getSkip(n);
final q = s['selector'] as String;
- final e = s['expect'] as List;
+ final e = s['expect'] as List?;
if ((s['exclude'] is! List ||
(s['exclude'].indexOf(nodeType) == -1 &&
s['exclude'].indexOf(docType) == -1)) &&
(s['testType'] & testType != 0)) {
//console.log("Running tests " + nodeType + ": " + s["testType"] + "&" + testType + "=" + (s["testType"] & testType) + ": " + JSON.stringify(s))
- List<Element> foundall;
- Element found;
+ late List<Element> foundall;
+ Element? found;
runTest(() {
- foundall = root.querySelectorAll(q) as List<Element>;
+ foundall = (root as dynamic).querySelectorAll(q) as List<Element>;
assertNotEquals(foundall, null, 'The method should not return null.');
- assertEquals(foundall.length, e.length,
+ assertEquals(foundall.length, e!.length,
'The method should return the expected number of matches.');
for (var i = 0; i < e.length; i++) {
@@ -244,15 +244,15 @@
}, '$type.querySelectorAll: $n:$q', skip: skip);
runTest(() {
- found = root.querySelector(q) as Element;
+ found = (root as dynamic).querySelector(q) as Element?;
- if (e.isNotEmpty) {
+ if (e!.isNotEmpty) {
assertNotEquals(found, null, 'The method should return a match.');
- assertEquals(found.attributes['id'], e[0],
+ assertEquals(found!.attributes['id'], e[0],
'The method should return the first match.');
assertEquals(found, foundall[0],
'The result should match the first item from querySelectorAll.');
- assertFalse(found.attributes.containsKey('data-clone'),
+ assertFalse(found!.attributes.containsKey('data-clone'),
'This should not be annotated as a cloned element.');
} else {
assertEquals(found, null, 'The method should not match anything.');
@@ -300,7 +300,7 @@
}
}
-void runTest(dynamic Function() body, String name, {String skip}) =>
+void runTest(dynamic Function() body, String name, {String? skip}) =>
unittest.test(name, body, skip: skip);
void assertTrue(bool value, String reason) =>
@@ -314,5 +314,5 @@
void assertNotEquals(x, y, String reason) =>
unittest.expect(x, unittest.isNot(y), reason: reason);
-void assertThrows(exception, void Function() body, [String reason]) =>
+void assertThrows(exception, void Function() body, [String? reason]) =>
unittest.expect(body, unittest.throwsA(exception), reason: reason);
diff --git a/test/support.dart b/test/support.dart
index 60a248a..83894af 100644
--- a/test/support.dart
+++ b/test/support.dart
@@ -12,8 +12,8 @@
typedef TreeBuilderFactory = TreeBuilder Function(bool namespaceHTMLElements);
-Map<String, TreeBuilderFactory> _treeTypes;
-Map<String, TreeBuilderFactory> get treeTypes {
+Map<String, TreeBuilderFactory>? _treeTypes;
+Map<String, TreeBuilderFactory>? get treeTypes {
// TODO(jmesserly): add DOM here once it's implemented
_treeTypes ??= {'simpletree': (useNs) => TreeBuilder(useNs)};
return _treeTypes;
@@ -37,7 +37,7 @@
// TODO(jmesserly): make this class simpler. We could probably split on
// "\n#" instead of newline and remove a lot of code.
-class TestData extends IterableBase<Map<String, String>> {
+class TestData extends IterableBase<Map<String?, String>> {
final String _text;
final String newTestHeading;
@@ -48,12 +48,12 @@
// Note: in Python this was a generator, but since we can't do that in Dart,
// it's easier to convert it into an upfront computation.
@override
- Iterator<Map<String, String>> get iterator => _getData().iterator;
+ Iterator<Map<String?, String>> get iterator => _getData().iterator;
- List<Map<String, String>> _getData() {
+ List<Map<String?, String>> _getData() {
var data = <String, String>{};
- String key;
- final result = <Map<String, String>>[];
+ String? key;
+ final List<Map<String?, String>> result = <Map<String, String>>[];
final lines = _text.split('\n');
// Remove trailing newline to match Python
if (lines.last == '') {
@@ -64,7 +64,7 @@
if (heading != null) {
if (data.isNotEmpty && heading == newTestHeading) {
// Remove trailing newline
- data[key] = data[key].substring(0, data[key].length - 1);
+ data[key!] = data[key]!.substring(0, data[key]!.length - 1);
result.add(normaliseOutput(data));
data = <String, String>{};
}
@@ -83,7 +83,7 @@
/// If the current heading is a test section heading return the heading,
/// otherwise return null.
- static String sectionHeading(String line) {
+ static String? sectionHeading(String line) {
return line.startsWith('#') ? line.substring(1).trim() : null;
}
@@ -173,7 +173,7 @@
for (var key in keys) {
final v = node.attributes[key];
if (key is AttributeName) {
- final attr = key as AttributeName;
+ final attr = key;
key = '${attr.prefix} ${attr.name}';
}
_newline();
diff --git a/test/tokenizer_test.dart b/test/tokenizer_test.dart
index 0ae5cab..87ba6d0 100644
--- a/test/tokenizer_test.dart
+++ b/test/tokenizer_test.dart
@@ -14,18 +14,18 @@
import 'support.dart';
class TokenizerTestParser {
- final String _state;
- final String _lastStartTag;
+ final String? _state;
+ final String? _lastStartTag;
final bool _generateSpans;
- List outputTokens;
+ List? outputTokens;
- TokenizerTestParser(String initialState,
- [String lastStartTag, bool generateSpans = false])
+ TokenizerTestParser(String? initialState,
+ [String? lastStartTag, bool generateSpans = false])
: _state = initialState,
_lastStartTag = lastStartTag,
_generateSpans = generateSpans;
- List parse(String str) {
+ List? parse(String str) {
// Note: we need to pass bytes to the tokenizer if we want it to handle BOM.
final bytes = utf8.encode(str);
final tokenizer =
@@ -36,7 +36,7 @@
// create a new closure to invoke it via mirrors.
final mtok = reflect(tokenizer);
tokenizer.state =
- () => mtok.invoke(Symbol(_state), const []).reflectee as bool;
+ () => mtok.invoke(Symbol(_state!), const []).reflectee as bool;
if (_lastStartTag != null) {
tokenizer.currentToken = StartTagToken(_lastStartTag);
@@ -108,10 +108,10 @@
}
void addOutputToken(Token token, List array) {
- outputTokens.add([
+ outputTokens!.add([
...array,
- if (token.span != null && _generateSpans) token.span.start.offset,
- if (token.span != null && _generateSpans) token.span.end.offset,
+ if (token.span != null && _generateSpans) token.span!.start.offset,
+ if (token.span != null && _generateSpans) token.span!.end.offset,
]);
}
}
@@ -151,7 +151,7 @@
/// positions of parse errors and non parse errors.
void expectTokensMatch(
List expectedTokens, List receivedTokens, bool ignoreErrorOrder,
- [bool ignoreErrors = false, String message]) {
+ [bool ignoreErrors = false, String? message]) {
// If the 'selfClosing' attribute is not included in the expected test tokens,
// remove it from the received token.
var removeSelfClosing = false;
@@ -201,10 +201,10 @@
testInfo['lastStartTag'] = null;
}
final parser = TokenizerTestParser(
- testInfo['initialState'] as String,
- testInfo['lastStartTag'] as String,
- testInfo['generateSpans'] as bool /*?*/ ?? false);
- var tokens = parser.parse(testInfo['input'] as String);
+ testInfo['initialState'] as String?,
+ testInfo['lastStartTag'] as String?,
+ testInfo['generateSpans'] as bool? ?? false);
+ var tokens = parser.parse(testInfo['input'] as String)!;
tokens = concatenateCharacterTokens(tokens);
final received = normalizeTokens(tokens);
final errorMsg = [
@@ -217,7 +217,7 @@
'\nreceived:',
tokens
].map((s) => '$s').join('\n');
- final ignoreErrorOrder = testInfo['ignoreErrorOrder'] as bool /*?*/ ?? false;
+ final ignoreErrorOrder = testInfo['ignoreErrorOrder'] as bool? ?? false;
expectTokensMatch(expected, received, ignoreErrorOrder, true, errorMsg);
}
@@ -252,7 +252,7 @@
final result = StringBuffer();
for (var match in RegExp(r'\W+(\w)(\w+)').allMatches(s)) {
if (result.length == 0) result.write(s.substring(0, match.start));
- result.write(match.group(1).toUpperCase());
+ result.write(match.group(1)!.toUpperCase());
result.write(match.group(2));
}
return result.toString();
@@ -265,7 +265,7 @@
final text = File(path).readAsStringSync();
final tests = jsonDecode(text);
final testName = pathos.basenameWithoutExtension(path);
- final testList = tests['tests'] as List;
+ final testList = tests['tests'] as List?;
if (testList == null) continue;
group(testName, () {