Remove Marker as a name for null (#136)

The checks against `== Marker` won't upgrade a nullable variable to
non-nullable like `== null` would.

- Replace `Marker` with `null`. Remove the field.
- Add a doc comment on the class to link to the parsing spec and
  describe the usage of `null`.
- Add a doc comment on `add` to describe it's difference from the normal
  `List.add`.
- Add some comments and a link to an issue filed about a likely bug in
  this method.
diff --git a/lib/parser.dart b/lib/parser.dart
index 940753c..363aa23 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -1504,7 +1504,7 @@
 
     final matchingElements = [];
     for (Node node in tree.activeFormattingElements.reversed) {
-      if (node == Marker) {
+      if (node == null) {
         break;
       } else if (isMatchingFormattingElement(node as Element, element)) {
         matchingElements.add(node);
@@ -1743,7 +1743,7 @@
   void startTagAppletMarqueeObject(StartTagToken token) {
     tree.reconstructActiveFormattingElements();
     tree.insertElement(token);
-    tree.activeFormattingElements.add(Marker);
+    tree.activeFormattingElements.add(null);
     parser.framesetOK = false;
   }
 
@@ -2434,7 +2434,7 @@
 
   void startTagCaption(StartTagToken token) {
     clearStackToTableContext();
-    tree.activeFormattingElements.add(Marker);
+    tree.activeFormattingElements.add(null);
     tree.insertElement(token);
     parser.phase = parser._inCaptionPhase;
   }
@@ -3054,7 +3054,7 @@
     clearStackToTableRowContext();
     tree.insertElement(token);
     parser.phase = parser._inCellPhase;
-    tree.activeFormattingElements.add(Marker);
+    tree.activeFormattingElements.add(null);
   }
 
   Token startTagTableOther(StartTagToken token) {
diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart
index a1f0da1..3687464 100644
--- a/lib/src/treebuilder.dart
+++ b/lib/src/treebuilder.dart
@@ -10,29 +10,33 @@
 import 'token.dart';
 import 'utils.dart';
 
-// The scope markers are inserted when entering object elements,
-// marquees, table cells, and table captions, and are used to prevent formatting
-// from "leaking" into tables, object elements, and marquees.
-const Element Marker = null;
-
-// TODO(jmesserly): this should extend ListBase<Element>, but my simple attempt
-// didn't work.
-class ActiveFormattingElements extends ListProxy<Element> {
-  // Override the "add" method.
-  // TODO(jmesserly): I'd rather not override this; can we do this in the
-  // calling code instead?
+/// Open elements in the formatting category, most recent element last.
+///
+/// `null` is used as the "marker" entry to prevent style from leaking when
+/// entering some elements.
+///
+/// https://html.spec.whatwg.org/multipage/parsing.html#list-of-active-formatting-elements
+class ActiveFormattingElements extends ListProxy<Element /*?*/ > {
+  /// Push an element into the active formatting elements.
+  ///
+  /// Prevents equivalent elements from appearing more than 3 times following
+  /// the last `null` marker. If adding [node] would cause there to be more than
+  /// 3 equivalent elements the earliest identical element is removed.
+  // TODO - Earliest equivalent following a marker, as opposed to earliest
+  // identical regardless of marker position, should be removed.
   @override
   void add(Element node) {
     var equalCount = 0;
-    if (node != Marker) {
+    if (node != null) {
       for (var element in reversed) {
-        if (element == Marker) {
+        if (element == null) {
           break;
         }
         if (_nodesEqual(element, node)) {
           equalCount += 1;
         }
         if (equalCount == 3) {
+          // TODO - https://github.com/dart-lang/html/issues/135
           remove(element);
           break;
         }
@@ -165,12 +169,12 @@
     // Step 2 and step 3: we start with the last element. So i is -1.
     var i = activeFormattingElements.length - 1;
     var entry = activeFormattingElements[i];
-    if (entry == Marker || openElements.contains(entry)) {
+    if (entry == null || openElements.contains(entry)) {
       return;
     }
 
     // Step 6
-    while (entry != Marker && !openElements.contains(entry)) {
+    while (entry != null && !openElements.contains(entry)) {
       if (i == 0) {
         //This will be reset to 0 below
         i = -1;
@@ -209,7 +213,7 @@
 
   void clearActiveFormattingElements() {
     var entry = activeFormattingElements.removeLast();
-    while (activeFormattingElements.isNotEmpty && entry != Marker) {
+    while (activeFormattingElements.isNotEmpty && entry != null) {
       entry = activeFormattingElements.removeLast();
     }
   }
@@ -221,7 +225,7 @@
     for (var item in activeFormattingElements.reversed) {
       // Check for Marker first because if it's a Marker it doesn't have a
       // name attribute.
-      if (item == Marker) {
+      if (item == null) {
         break;
       } else if (item.localName == name) {
         return item;