Nullable document/head/body
diff --git a/lib/dom.dart b/lib/dom.dart
index 8e0ea0b..cb4d63a 100644
--- a/lib/dom.dart
+++ b/lib/dom.dart
@@ -317,9 +317,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.
diff --git a/test/dom_test.dart b/test/dom_test.dart
index f9a5cac..82db4f4 100644
--- a/test/dom_test.dart
+++ b/test/dom_test.dart
@@ -24,7 +24,7 @@
         '<div id=Foo>');
 
     test('getElementById', () {
-      final foo = doc.body.nodes[0];
+      final foo = doc.body!.nodes[0];
       final fooVar = foo.nodes[2];
       expect((foo as Element).id, 'foo');
       expect((fooVar as Element).id, 'Foo');
@@ -33,7 +33,7 @@
     });
 
     test('getElementsByClassName', () {
-      final foo = doc.body.nodes[0];
+      final foo = doc.body!.nodes[0];
       final barBaz = foo.nodes[0];
       final quxBaz = foo.nodes[1];
       expect((barBaz as Element).className, ' bar baz');
@@ -45,7 +45,7 @@
     });
 
     test('getElementsByTagName', () {
-      final foo = doc.body.nodes[0];
+      final foo = doc.body!.nodes[0];
       final barBaz = foo.nodes[0];
       final quxBaz = foo.nodes[1];
       final fooVar = foo.nodes[2];
@@ -56,135 +56,135 @@
   group('fragments are flattened', () {
     test('add', () {
       final doc = parse('<body>');
-      doc.body.nodes.add(parseFragment('<x-foo>'));
-      expect((doc.body.nodes[0] as Element).localName, 'x-foo');
-      doc.body.nodes.add(parseFragment('<x-bar>'));
-      expect((doc.body.nodes[1] as Element).localName, 'x-bar');
+      doc.body!.nodes.add(parseFragment('<x-foo>'));
+      expect((doc.body!.nodes[0] as Element).localName, 'x-foo');
+      doc.body!.nodes.add(parseFragment('<x-bar>'));
+      expect((doc.body!.nodes[1] as Element).localName, 'x-bar');
     });
 
     test('addLast', () {
       final doc = parse('<body>');
-      doc.body.nodes.addLast(parseFragment('<x-foo>'));
-      expect((doc.body.nodes[0] as Element).localName, 'x-foo');
-      doc.body.nodes.addLast(parseFragment('<x-bar>'));
-      expect((doc.body.nodes[1] as Element).localName, 'x-bar');
+      doc.body!.nodes.addLast(parseFragment('<x-foo>'));
+      expect((doc.body!.nodes[0] as Element).localName, 'x-foo');
+      doc.body!.nodes.addLast(parseFragment('<x-bar>'));
+      expect((doc.body!.nodes[1] as Element).localName, 'x-bar');
     });
 
     test('addAll', () {
       final doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.addAll([parseFragment('<x-b></x-b><x-c></x-c>')]);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'x-b');
-      expect((doc.body.nodes[2] as Element).localName, 'x-c');
+      doc.body!.nodes.addAll([parseFragment('<x-b></x-b><x-c></x-c>')]);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-c');
     });
 
     test('insert', () {
       var doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.insert(0, parseFragment('<x-b></x-b><x-c></x-c>'));
-      expect((doc.body.nodes[0] as Element).localName, 'x-b');
-      expect((doc.body.nodes[1] as Element).localName, 'x-c');
-      expect((doc.body.nodes[2] as Element).localName, 'x-a');
+      doc.body!.nodes.insert(0, parseFragment('<x-b></x-b><x-c></x-c>'));
+      expect((doc.body!.nodes[0] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-c');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-a');
 
       doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.insert(1, parseFragment('<x-b></x-b><x-c></x-c>'));
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'x-b');
-      expect((doc.body.nodes[2] as Element).localName, 'x-c');
+      doc.body!.nodes.insert(1, parseFragment('<x-b></x-b><x-c></x-c>'));
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-c');
 
       doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.insert(0, parseFragment('<x-b></x-b>'));
-      doc.body.nodes.insert(1, parseFragment('<x-c></x-c>'));
-      expect((doc.body.nodes[0] as Element).localName, 'x-b');
-      expect((doc.body.nodes[1] as Element).localName, 'x-c');
-      expect((doc.body.nodes[2] as Element).localName, 'x-a');
+      doc.body!.nodes.insert(0, parseFragment('<x-b></x-b>'));
+      doc.body!.nodes.insert(1, parseFragment('<x-c></x-c>'));
+      expect((doc.body!.nodes[0] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-c');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-a');
     });
 
     test('insertAll', () {
       var doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.insertAll(0, [parseFragment('<x-b></x-b><x-c></x-c>')]);
-      expect((doc.body.nodes[0] as Element).localName, 'x-b');
-      expect((doc.body.nodes[1] as Element).localName, 'x-c');
-      expect((doc.body.nodes[2] as Element).localName, 'x-a');
-      expect(doc.body.nodes.length, 3);
+      doc.body!.nodes.insertAll(0, [parseFragment('<x-b></x-b><x-c></x-c>')]);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-c');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-a');
+      expect(doc.body!.nodes.length, 3);
 
       doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.insertAll(1, [parseFragment('<x-b></x-b><x-c></x-c>')]);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'x-b');
-      expect((doc.body.nodes[2] as Element).localName, 'x-c');
+      doc.body!.nodes.insertAll(1, [parseFragment('<x-b></x-b><x-c></x-c>')]);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-c');
 
       doc = parse('<body><x-a></x-a>');
-      doc.body.nodes.insertAll(0, [parseFragment('<x-b></x-b>')]);
-      doc.body.nodes.insertAll(1, [parseFragment('<x-c></x-c>')]);
-      expect((doc.body.nodes[0] as Element).localName, 'x-b');
-      expect((doc.body.nodes[1] as Element).localName, 'x-c');
-      expect((doc.body.nodes[2] as Element).localName, 'x-a');
+      doc.body!.nodes.insertAll(0, [parseFragment('<x-b></x-b>')]);
+      doc.body!.nodes.insertAll(1, [parseFragment('<x-c></x-c>')]);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-c');
+      expect((doc.body!.nodes[2] as Element).localName, 'x-a');
     });
 
     test('operator []=', () {
       var doc = parse('<body><x-a></x-a>');
-      doc.body.nodes[0] = parseFragment('<x-b></x-b><x-c></x-c>');
-      expect((doc.body.nodes[0] as Element).localName, 'x-b');
-      expect((doc.body.nodes[1] as Element).localName, 'x-c');
-      expect(doc.body.nodes.length, 2);
+      doc.body!.nodes[0] = parseFragment('<x-b></x-b><x-c></x-c>');
+      expect((doc.body!.nodes[0] as Element).localName, 'x-b');
+      expect((doc.body!.nodes[1] as Element).localName, 'x-c');
+      expect(doc.body!.nodes.length, 2);
 
       doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
-      doc.body.nodes[1] = parseFragment('<y-b></y-b><y-c></y-c>');
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'y-b');
-      expect((doc.body.nodes[2] as Element).localName, 'y-c');
-      expect((doc.body.nodes[3] as Element).localName, 'x-c');
-      expect(doc.body.nodes.length, 4);
+      doc.body!.nodes[1] = parseFragment('<y-b></y-b><y-c></y-c>');
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'y-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'y-c');
+      expect((doc.body!.nodes[3] as Element).localName, 'x-c');
+      expect(doc.body!.nodes.length, 4);
     });
 
     test('setRange', () {
       var fragment = parseFragment('<y-b></y-b><y-c></y-c>');
       var doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
-      doc.body.nodes.setRange(1, 2, fragment.nodes, 0);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'y-b');
-      expect((doc.body.nodes[2] as Element).localName, 'y-c');
-      expect(doc.body.nodes.length, 3);
+      doc.body!.nodes.setRange(1, 2, fragment.nodes, 0);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'y-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'y-c');
+      expect(doc.body!.nodes.length, 3);
 
       fragment = parseFragment('<y-b></y-b><y-c></y-c>');
       doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
-      doc.body.nodes.setRange(1, 1, [fragment], 0);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'y-b');
-      expect((doc.body.nodes[2] as Element).localName, 'y-c');
-      expect((doc.body.nodes[3] as Element).localName, 'x-c');
-      expect(doc.body.nodes.length, 4);
+      doc.body!.nodes.setRange(1, 1, [fragment], 0);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'y-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'y-c');
+      expect((doc.body!.nodes[3] as Element).localName, 'x-c');
+      expect(doc.body!.nodes.length, 4);
     });
 
     test('replaceRange', () {
       var fragment = parseFragment('<y-b></y-b><y-c></y-c>');
       var doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
-      doc.body.nodes.replaceRange(1, 2, fragment.nodes);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'y-b');
-      expect((doc.body.nodes[2] as Element).localName, 'y-c');
-      expect((doc.body.nodes[3] as Element).localName, 'x-c');
-      expect(doc.body.nodes.length, 4);
+      doc.body!.nodes.replaceRange(1, 2, fragment.nodes);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'y-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'y-c');
+      expect((doc.body!.nodes[3] as Element).localName, 'x-c');
+      expect(doc.body!.nodes.length, 4);
 
       fragment = parseFragment('<y-b></y-b><y-c></y-c>');
       doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
-      doc.body.nodes.replaceRange(1, 2, [fragment]);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'y-b');
-      expect((doc.body.nodes[2] as Element).localName, 'y-c');
-      expect((doc.body.nodes[3] as Element).localName, 'x-c');
-      expect(doc.body.nodes.length, 4);
+      doc.body!.nodes.replaceRange(1, 2, [fragment]);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'y-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'y-c');
+      expect((doc.body!.nodes[3] as Element).localName, 'x-c');
+      expect(doc.body!.nodes.length, 4);
     });
 
     test('replaceWith', () {
       final fragment = parseFragment('<y-b></y-b><y-c></y-c>');
       final doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
-      doc.body.nodes[1].replaceWith(fragment);
-      expect((doc.body.nodes[0] as Element).localName, 'x-a');
-      expect((doc.body.nodes[1] as Element).localName, 'y-b');
-      expect((doc.body.nodes[2] as Element).localName, 'y-c');
-      expect((doc.body.nodes[3] as Element).localName, 'x-c');
-      expect(doc.body.nodes.length, 4);
+      doc.body!.nodes[1].replaceWith(fragment);
+      expect((doc.body!.nodes[0] as Element).localName, 'x-a');
+      expect((doc.body!.nodes[1] as Element).localName, 'y-b');
+      expect((doc.body!.nodes[2] as Element).localName, 'y-c');
+      expect((doc.body!.nodes[3] as Element).localName, 'x-c');
+      expect(doc.body!.nodes.length, 4);
     });
   });
 }
diff --git a/test/parser_feature_test.dart b/test/parser_feature_test.dart
index 7825a23..64068d0 100644
--- a/test/parser_feature_test.dart
+++ b/test/parser_feature_test.dart
@@ -20,7 +20,7 @@
   test('line counter', () {
     // http://groups.google.com/group/html5lib-discuss/browse_frm/thread/f4f00e4a2f26d5c0
     final doc = parse('<pre>\nx\n&gt;\n</pre>');
-    expect(doc.body.innerHtml, '<pre>x\n&gt;\n</pre>');
+    expect(doc.body!.innerHtml, '<pre>x\n&gt;\n</pre>');
   });
 
   test('namespace html elements on', () {
@@ -43,7 +43,7 @@
 </html>
 ''', generateSpans: true, sourceUrl: 'ParseError');
     final doc = parser.parse();
-    expect(doc.body.outerHtml, '<body>\n  \n  \n\n</body>');
+    expect(doc.body!.outerHtml, '<body>\n  \n  \n\n</body>');
     expect(parser.errors.length, 1);
     final error = parser.errors[0];
     expect(error.errorCode, 'unexpected-doctype');
@@ -73,7 +73,7 @@
 </html>
 ''');
     final doc = parser.parse();
-    expect(doc.body.outerHtml, '<body>\n  \n  \n\n</body>');
+    expect(doc.body!.outerHtml, '<body>\n  \n  \n\n</body>');
     expect(parser.errors.length, 1);
     final error = parser.errors[0];
     expect(error.errorCode, 'unexpected-doctype');
@@ -86,7 +86,7 @@
     final textContent = '\n  hello {{name}}';
     final html = '<body><div>$textContent</div>';
     final doc = parse(html, generateSpans: true);
-    final text = doc.body.nodes[0].nodes[0] as Text;
+    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));
@@ -150,26 +150,26 @@
 
   test('void element innerHTML', () {
     var doc = parse('<div></div>');
-    expect(doc.body.innerHtml, '<div></div>');
+    expect(doc.body!.innerHtml, '<div></div>');
     doc = parse('<body><script></script></body>');
-    expect(doc.body.innerHtml, '<script></script>');
+    expect(doc.body!.innerHtml, '<script></script>');
     doc = parse('<br>');
-    expect(doc.body.innerHtml, '<br>');
+    expect(doc.body!.innerHtml, '<br>');
     doc = parse('<br><foo><bar>');
-    expect(doc.body.innerHtml, '<br><foo><bar></bar></foo>');
+    expect(doc.body!.innerHtml, '<br><foo><bar></bar></foo>');
   });
 
   test('empty document has html, body, and head', () {
     final doc = parse('');
     final html = '<html><head></head><body></body></html>';
     expect(doc.outerHtml, html);
-    expect(doc.documentElement.outerHtml, html);
-    expect(doc.head.outerHtml, '<head></head>');
-    expect(doc.body.outerHtml, '<body></body>');
+    expect(doc.documentElement!.outerHtml, html);
+    expect(doc.head!.outerHtml, '<head></head>');
+    expect(doc.body!.outerHtml, '<body></body>');
   });
 
   test('strange table case', () {
-    final doc = parse('<table><tbody><foo>').body;
+    final doc = parse('<table><tbody><foo>').body!;
     expect(doc.innerHtml, '<foo></foo><table><tbody></tbody></table>');
   });
 
@@ -177,7 +177,7 @@
     test('attribute order', () {
       // Note: the spec only requires a stable order.
       // However, we preserve the input order via LinkedHashMap
-      final body = parse('<foo d=1 a=2 c=3 b=4>').body;
+      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.innerHtml, '<foo d="1" c="3" b="4"></foo>');
@@ -249,7 +249,7 @@
   test('error printing without spans', () {
     final parser = HtmlParser('foo');
     final doc = parser.parse();
-    expect(doc.body.innerHtml, 'foo');
+    expect(doc.body!.innerHtml, 'foo');
     expect(parser.errors.length, 1);
     expect(parser.errors[0].errorCode, 'expected-doctype-but-got-chars');
     expect(parser.errors[0].message,
diff --git a/test/selectors/level1_baseline_test.dart b/test/selectors/level1_baseline_test.dart
index cba4c21..5e85c8f 100644
--- a/test/selectors/level1_baseline_test.dart
+++ b/test/selectors/level1_baseline_test.dart
@@ -116,7 +116,7 @@
 
   group('out of scope', () {
     setUp(() {
-      doc.body.append(outOfScope); // Append before in-document Element tests.
+      doc.body!.append(outOfScope); // Append before in-document Element tests.
       // None of these elements should match
     });
     tearDown(outOfScope.remove);